java方法句柄--MethodHandle

Java7中为间接调用方法引入了新的api,即 方法句柄。
方法句柄中包含两个重要的类,MethodHandle和MethodType.

MethodType用来描述方法的返回值类型以及入参类型。
MehodHandle包含一个指向Method对象(方法在jvm内部的对等体)的指针。

而MehodHandle对象有2个重要方法invoke和invokeExact.

MethodHandle获得的方法引用,并不能 突破访问权限本身的限制,比如private方法,就不能在类外被使用,这一点不如反射。
但是也可以暴露方法句柄给外部使用,比如下面这个例子:

就通过方法句柄,在类Study1中调用了类Test1的私有方法。

public class Test1 extends p{
    public int a=0;
    static Integer si=6;
    String s="Hello world";

    public Test1(int d){

    }

    @Override
    public void say1() {
        System.out.println("t");
    }

    private void do1(){
        a++;
        System.out.println(a);

    }
}

public class Study1 {
    public static void main(String[] args) throws Throwable {
        MethodType methodType=MethodType.methodType(Void.TYPE);


        Test1 test1=new Test1(4);


//        MethodHandle methodHandle2= MethodHandles.lookup().findVirtual(
//                Test1.class,"do1",methodType
//        );

        MethodHandle methodHandle2=Test1.getMethodHandle();
        methodHandle2.invokeExact(test1);
    }
    public void do1(){
        do2();
    }
    public void do2(){
        System.out.println("1");
    }
}

关于invoke和invokeExtract方法的区别:
invokeExtract要求更加精确,
如下 methodHandle2.invokeExact(test1,5.1,new Integer(1));可以执行,
methodHandle2.invokeExact(test1,5.1,1);会报错,因为要将1转换为integer,所以不合要求。这个方法要求不能有任何类型转换,也就是参数严格一致。
invoke相对要轻松很多。

还有一点,如果MehodHandle取得的是父类方法句柄的指针,然后调用时,第一个参数(也就是调用方)是子类的话,invokestactic会失败(也就是说它其实还要求mehodhandle所属的Class对象与 调用方实例是一个类型)。
而invoke不会做这个检查,也就是说即使句柄是父类的方法句柄,如果调用方是子类实例的话,它实际运行时会调用子类相应的覆盖的方法(推测可能是使用methodHandle在vtabl虚方法表中的索引来快速调用的)。

这个例子可以证明上面这个结论,虽然通过方法句柄拿到了方法的引用,但是如果
子类Test1中覆盖了say1方法,那么最终被调用的是子类的say1。但是很明显,方
法引用其实是指向的父类的方法。所以invoke是通过调用方的虚方法表来调用实际方法的。

而invokestaic则要求“methodHanle的调用方的Class”与“mehodHandle所引用
的method所属的Class”必须一致。


   public static void main(String[] args) throws Throwable {
        MethodType methodType=MethodType.methodType(Void.TYPE);

        MethodHandle methodHandle2= MethodHandles.lookup().findVirtual(
                p.class,"say1",methodType
        );
        Test1 test1=new Test1(4);
        methodHandle2.invoke(test1);
        }
private void do1(double b,Integer integer){
        a++;
        System.out.println("do1 id");

    }
 public static void main(String[] args) throws Throwable {
        MethodType methodType=MethodType.methodType(Void.TYPE,double.class,Integer.class);
        
        MethodHandle methodHandle2= MethodHandles.lookup().findVirtual(
                Test1.class,"do1",methodType
        );
        Test1 test1=new Test1(4);
        methodHandle2.invokeExact(test1,5.1,new Integer(1));
        methodHandle2.invokeExact(test1,5.1,1);
        }

猜你喜欢

转载自blog.csdn.net/ljz2016/article/details/83097461
今日推荐