简体   繁体   English

我想打印 hi GrandFather;但它似乎打印 hifather

[英]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我不明白如何findSpecialfindVirtual之间的使用

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.来自Sonsuper调用只能在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指定为上下文类,因为允许FatherGrandFather执行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.这是有效的,因为SonFather是同一个顶级类的内部类,因此允许访问彼此的私有成员。 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.FatherSon在同一个模块中或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 classFindSpecialFailure ,因此,这将用于判断来自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.

相关问题 嗨,我想打印用户输入的 2 个数字之间的所有回文素数 - Hi I want to print all the palindromic primes between 2 numbers the user inputs 嗨,我似乎无法打印将使用DrawingPanel比较三个圆圈大小的程序的结果 - Hi, I cant seem to print the results of a program that will compare three circles size using DrawingPanel 嗨,我想从我的应用中禁用(删除)这些权限 - Hi i want to disable (delete) these permissions from my app 嗨,我想通过单击菜单从汉堡更改为交叉 - Hi i want to change from hamburgur to cross on click of the menu 嗨,我想使用math.random设置重复的可能性 - Hi I want to use math.random to set the likely for it to repeat 嗨,我想用 thyleaf 填充 html 的 select 并出现错误 - Hi, I want fill an select of html with thyleaf and appears an Error 嗨,尝试根据数组值打印一定数量的字符。 Java的 - Hi, trying to print a certain amount of character based on array values. Java 您好,我有一个列表视图,其中包含要使用onitemclick方法打开另一个列表的元素列表 - hi i have a list view that contains list of elements i want to use onitemclick method to open another list 大家好,我想使用Java代码在我的文本文件中搜索单词“((错误:87)”) - Hi All, I want to search this word “(Error: 87)” in my text file using Java code 嗨,我有一个 findViewById 错误 - HI.I have an error with findViewById
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM