[英]I want to print hi GrandFather;but it seems to print hi father
I want to print hi GrandFather
我想打印
hi GrandFather
But it seems to print hi father.但它似乎打印喜父亲。 And I am not understand how to diff the use between
findSpecial
and findVirtual
我不明白如何
findSpecial
和findVirtual
之间的使用
I want someone can help me.我想要有人可以帮助我。 Thank you
谢谢
class GrandFather{
void thinking(){
System.out.println("hi GrandFather");
}
}
class Father extends GrandFather{
void thinking(){
System.out.println("hi Father");
}
}
class Son extends Father{
void thinking(){
MethodType mt=MethodType.methodType(void.class);
//MethodHandle mh=MethodHandles.lookup().findVirtual(GrandFather.class,"thinking",mt).bindTo(this);
MethodHandle mh;
mh = MethodHandles.lookup().findSpecial(GrandFather.class,"thinking",mt,getClass());
mh.invoke(this);
}
}
public static void main(String[] args){
(new MethodHandleTest().new Son()).thinking();
}
The last argument to findSpecial
specifies the context class for the invocation. findSpecial
的最后一个参数指定调用的上下文类。 You specified getClass()
, which will result in Son.class
.您指定了
getClass()
,这将导致Son.class
。 A super
invocation from Son
can only end up in Father
, just like an ordinary super.thinking()
call in source code.来自
Son
的super
调用只能在Father
结束,就像源代码中的普通super.thinking()
调用一样。
You need to specify Father.class
as context class, as Father
is allowed to perform a super
invocation to GrandFather
.您需要将
Father.class
指定为上下文类,因为允许Father
对GrandFather
执行super
调用。 If you do this without further changes, you'd get an exception like java.lang.IllegalAccessException: no private access for invokespecial…
.如果您在没有进一步更改的情况下执行此操作,您将收到类似
java.lang.IllegalAccessException: no private access for invokespecial…
的异常。 You have to change the context of your lookup()
object, to be able to access private
features of Father
:您必须更改您的
lookup()
对象的上下文,才能访问Father
private
功能:
MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = MethodHandles.lookup().in(Father.class)
.findSpecial(GrandFather.class, "thinking", mt, Father.class);
mh.invoke(this);
This works, because Son
and Father
are inner classes of the same top level class, so accessing each other's private members is allowed.这是有效的,因为
Son
和Father
是同一个顶级类的内部类,因此允许访问彼此的私有成员。 If they were not classes within the same top level class, in(…)
would change the context class but clear the private access permission.如果它们不是同一顶级类中的类,则
in(…)
将更改上下文类但清除私有访问权限。 In that case, only Java 9 and newer have an official solution:在这种情况下,只有 Java 9 及更新版本有官方解决方案:
MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = MethodHandles.privateLookupIn(Father.class, MethodHandles.lookup())
.findSpecial(GrandFather.class, "thinking", mt, Father.class);
mh.invoke(this);
This works when Father
and Son
are in the same module or Father
's module has opened Father
's package to Son
's module for Reflection.当
Father
和Son
在同一个模块中或Father
的模块已将Father
的包打开到Son
的模块进行反射时,此方法有效。
Holger as usual is correct. Holger 像往常一样是正确的。 It actually took me a while to understand what is going on, please treat this as an amendment to the other very good answer.
我实际上花了一段时间才明白发生了什么,请将其视为对另一个非常好的答案的修正。 To understand this, I had to do 3 different examples.
为了理解这一点,我不得不做 3 个不同的例子。
public class FindSpecialFailure {
public static void main(String[] args) {
try {
MethodType mt = MethodType.methodType(void.class);
Lookup l = MethodHandles.lookup();
MethodHandle mh = l.findSpecial(Son.class, "go", mt, Son.class);
mh.invoke(new Son());
} catch (Throwable t) {
t.printStackTrace();
}
}
static class Parent {
void go() {
System.out.println("parent");
}
}
static class Son extends Parent {
void go() {
System.out.println("son");
}
}
}
If you run this, it will fail with java.lang.IllegalAccessException: no private access for invokespecial...
.如果你运行它,它会失败并显示
java.lang.IllegalAccessException: no private access for invokespecial...
。 The documentation gives proper directions of why this happens: 文档给出了为什么会发生这种情况的正确说明:
In general, the conditions under which a method handle may be looked up for a method M are no more restrictive than the conditions under which the lookup class could have compiled, verified, and resolved a call to M.
通常,可以为方法 M 查找方法句柄的条件并不比查找类可以编译、验证和解析对 M 的调用的条件更具限制性。
That same documentation explains this also:相同的文档也解释了这一点:
The caller class against which those restrictions are enforced is known as the lookup class .
强制执行这些限制的调用者类称为查找类。
In our case lookup class
is FindSpecialFailure
and as such, this would be used to be able to tell if method go
from Son.class
could be compiled, verified, and resolved .在我们的例子中,
lookup class
是FindSpecialFailure
,因此,这将用于判断来自Son.class
方法go
Son.class
可以被编译、验证和解析。
You can think about it simpler, a bit.你可以想得更简单一点。 Would you be able (in theory) to create an
invokeSpecial
byte code instruction in FindSpecialFailure::main
and invoke it?你能(理论上)在
FindSpecialFailure::main
创建一个invokeSpecial
字节码指令并调用它吗? Again, in theory.再次,理论上。 You could create it there:
你可以在那里创建它:
invokeSpecial Son.go:()V
Can you invoke it though?你能调用它吗? Well, no ;
嗯,没有; specifically, in my understanding, this rule would be broken:
具体来说,以我的理解,这条规则将被打破:
If the symbolic reference names a class (not an interface), then that class is a superclass of the current class.
如果符号引用命名一个类(不是接口),则该类是当前类的超类。
Obviously Son
is not a superclass of FindSpecialFailure
.显然
Son
不是FindSpecialFailure
的超类。
To prove my point, you could change the above code to (second example):为了证明我的观点,您可以将上面的代码更改为(第二个示例):
// example 1
public class FindSpecialInterface {
public static void main(String[] args) {
try {
MethodType mt = MethodType.methodType(void.class);
Lookup l = MethodHandles.lookup();
// <---- Parent.class
MethodHandle mh = l.findSpecial(Parent.class, "go", mt, Son.class);
mh.invoke(new Son());
} catch (Throwable t) {
t.printStackTrace();
}
}
// <---- this is now an interface
interface Parent {
default void go() {
System.out.println("parent");
}
}
static class Son implements Parent {
public void go() {
System.out.println("son");
}
}
}
This time everything will work just fine, because ( from the same specification ):这一次一切都会正常工作,因为( 来自相同的规范):
Otherwise, if C is an interface and the class Object contains a declaration of a public instance method with the same name and descriptor as the resolved method, then it is the method to be invoked.
否则,如果 C 是一个接口并且类 Object 包含一个与解析方法具有相同名称和描述符的公共实例方法的声明,那么它就是要调用的方法。
How do I fix the first example though?我如何修复第一个例子? (
FindSpecialFailure
) You need to add an interesting option: (
FindSpecialFailure
) 您需要添加一个有趣的选项:
public static void main(String[] args) {
try {
MethodType mt = MethodType.methodType(void.class);
// <--- Notice the .in(Son.class) -->
Lookup l = MethodHandles.lookup().in(Son.class);
MethodHandle mh = l.findSpecial(Son.class, "go", mt, Son.class);
mh.invoke(new Son());
} catch (Throwable t) {
t.printStackTrace();
}
}
If you go to the same documentation , you will find:如果您转到相同的文档,您会发现:
In some cases, access between nested classes is obtained by the Java compiler by creating an wrapper method to access a private method of another class in the same top-level declaration.
在某些情况下,嵌套类之间的访问是由 Java 编译器通过创建包装器方法来访问同一顶级声明中另一个类的私有方法来获得的。 For example, a nested class CD can access private members within other related classes such as C, CDE, or CB, but the Java compiler may need to generate wrapper methods in those related classes.
例如,嵌套类 CD 可以访问其他相关类(如 C、CDE 或 CB)中的私有成员,但 Java 编译器可能需要在这些相关类中生成包装方法。 In such cases, a Lookup object on CE would be unable to those private members.
在这种情况下,CE 上的 Lookup 对象将无法访问这些私有成员。 A workaround for this limitation is the Lookup.in method , which can transform a lookup on CE into one on any of those other classes, without special elevation of privilege.
此限制的一种解决方法是 Lookup.in 方法,它可以将 CE 上的查找转换为任何其他类上的查找,无需特殊提升特权。
The third example somehow starts to look more like your examples:第三个例子不知何故开始看起来更像你的例子:
public class FinSpecialMoveIntoSon {
public static void main(String[] args) {
new Son().invokeMe();
}
static class Parent {
public void go() {
System.out.println("parent");
}
}
static class Son extends Parent {
void invokeMe() {
try {
MethodType mt = MethodType.methodType(void.class);
Lookup l = MethodHandles.lookup();
MethodHandle mh = l.findSpecial(Son.class, "go", mt, Son.class);
mh.invoke(new Son());
} catch (Throwable t) {
t.printStackTrace();
}
}
public void go() {
System.out.println("son");
}
}
}
The point of this one is that the documentation of findSpecial
says about the first parameter:这个问题的重点是
findSpecial
的文档说明了第一个参数:
refc the class or interface from which the method is accessed.
refc 访问方法的类或接口。
That is why it will print Son
, and not Parent
.这就是为什么它会打印
Son
而不是Parent
。
Armed with this, your example becomes easier to understand:有了这个,你的例子就更容易理解了:
static class Son extends Father {
void thinking() {
try {
MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = MethodHandles.lookup().findSpecial(GrandFather.class, "thinking", mt, getClass());
mh.invoke(this);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
The lookup class is Son.class
and method resolution and refc
(there the class or interface from which the method is accessed ) is GranFather
.查找类是
Son.class
,方法解析和refc
(从那里访问方法的类或接口)是GranFather
。 So the resolution does start with GrandFather::thinking
, but since you can't call super.super
methods in java, that is "downgraded" to Father::thinking
.所以决议确实从
GrandFather::thinking
开始,但由于你不能在java中调用super.super
方法,所以它被“降级”为Father::thinking
。
All I could suggest here was to use .in
to solve this, I was not aware of privateLookupIn
.我在这里只能建议使用
.in
来解决这个问题,我不知道privateLookupIn
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.