《深入理解Java虚拟机(第2版)》使用MethodHandle来解决相关问题——错误更正

使用MethodHandle来解决相关问题——错误更正

在阅读《深入理解Java虚拟机(第2版)》第8章——虚拟机字节码执行引擎时,发现作者给出了一个关于MethodHandle来解决相关问题的例子。

我们直接给出代码,问题位于内部类SON中的注释。

package methodHandle;

import java.lang.invoke.MethodHandle;

import static java.lang.invoke.MethodHandles.lookup;

import java.lang.invoke.MethodType;

class HandleGFFunction {
        class GF{
            void thinking(){
                System.out.println("i am a grandfather function");
            }
        }
        class FA extends GF{
            void thinking(){
                System.out.println("i am a father function");
            }
        }
        class SON extends FA{
            void thinking(){
                //请在thinking()中填入适当的代码,不能修改其他地方的代码
                //实现调用祖父类的thinking()方法
                try{
                    MethodType mt = MethodType.methodType(void.class);
                    /*
                     * public MethodHandle findSpecial(Class refc,
                                String name,
                                MethodType type,
                                Class specialCaller)
                         throws NoSuchMethodException,
                                IllegalAccessException
                     */
                    MethodHandle mh = lookup().findSpecial(GF.class, "thinking", mt, getClass());
                    mh.invoke(this);
                    //System.out.println(getClass());
                }catch(Throwable e){

                }
            }
        }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        (new HandleGFFunction().new SON()).thinking();
    }

}


照着书本的代码敲了一遍,运行输出结果,发现总是输出:

i am a father function

仔细校对与书本的代码,发现并没有错误与不同。于是在想是不是fingSpecial()方法的问题,于是上知乎浏览了一番,最终发现了答案,贴下答主的解答。

作者:RednaxelaFX
链接:https://www.zhihu.com/question/40427344/answer/86545388
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

您自己实验得到的结果是正确的,而您参考的书所说的是错误的。
MethodHandle用于模拟invokespecial时,必须遵守跟Java字节码里的invokespecial指令相同的限制——它只能调用到传给findSpecial()方法的最后一个参数(“specialCaller”)的直接父类的版本。invokespecial指令的规定可以参考JVM规范:Chapter 6. The Java Virtual Machine Instruction Set,不过这部分写得比较“递归”所以不太直观。

findSpecial()还特别限制如果Lookup发现传入的最后一个参数(“specialCaller”)跟当前类不一致的话默认会马上抛异常:jdk8u/jdk8u/jdk: e2117e30fb39 src/share/classes/java/lang/invoke/MethodHandles.java

在这个例子里,Son <: Father <: GrandFather,而Father与GrandFather类上都有自己的thinking()方法的实现,因而从Son出发查找就会找到其直接父类Father上的thinking(),即便传给findSpecial()的第一个参数是GrandFather。

请参考文档:MethodHandles.Lookup (Java Platform SE 8 )-

题主所参考的书给的例子不正确,可能是因为findSpecial()得到的MethodHandle的具体语义在JSR 292的设计过程中有被调整过。有一段时间findSpecial()得到的MethodHandle确实可以超越invokespecial的限制去调用到任意版本的虚方法,但这种行为很快就被认为是bug而修正了。

你可能感兴趣的:(Java学习心得)