MethodHandle(方法句柄)系列之三:invoke和invokeExact的区别

  先把代码贴上来,用的是一样的代码
/**
 * 
 * @author LiuYeFeng<[email protected]>
 * @date 2015年4月8日 下午10:41:13
 * @CopyRight 2015 TopView Inc
 * @version V1.0
 */
public class MethodHandleTest {
 
    public MethodHandle getHandler() {
        MethodHandle mh = null;
        MethodType mt = MethodType.methodType(String.class, int.class, int.class);
        MethodHandles.Lookup lk = MethodHandles.lookup();
         
        try {
            mh = lk.findVirtual(String.class, "substring", mt);
        } catch (Throwable e) {
            e.printStackTrace();
        }
         
        return mh;
    }
     
    public static void main(String[] args) throws Throwable {
        MethodHandle mh = new MethodHandleTest().getHandler();
        String str = "hello world";
         
        Object result1 = mh.invoke(str, 1, 3);
        Object result2 = (String) mh.invokeExact(str, 1, 3);
//      Object result2 = mh.invokeExact(str, new Integer(1), 3); 
        /**
         * 上面这句方法执行时报错,因为方法类型为String.class, int.class, int.class
         * 而返回的类型为Object,与声明中为String不符合
         * 其中第二个参数类型为Integer,与声明中为int不符合,则类型适配不符合,系统报错。
         */
         
        System.out.println("result 1:" + result1);
        System.out.println("result 1:" + result2);
    }
}

    invoke和invokeExact方法的区别,从名字上来看,明显是后者准确性更高,或者说要求更严格。invokeExact方法在调用时要求严格的类型匹配,方法的返回值类型也在考虑范围之内,如同上面代码中注释掉的一句。如果把Object result2 = (String) mh.invokeExact(str, 13);中的(String)去掉类型转换的话,在调用的时候该方法会认为返回值是Object类型而不是String类型,然后系统报错。

       与invokeExact方法不同,invoke方法允许更加松散的调用方式。它会尝试在调用的时候进行返回值和参数类型的转换工作。这是通过MethodHandle类的asType方法来完成的,asType方法的作用是把当前方法句柄适配到新的MethodType上面,并产生一个新的方法句柄。当方法句柄在调用时的类型与其声明的类型完全一致的时候,调用invoke方法等于调用invokeExact方法;否则,invoke方法会先调用asType方法来尝试适配到调用时的类型。如果适配成功,则可以继续调用。否则会抛出相关的异常。这种灵活的适配机制,使invoke方法成为在绝大多数情况下都应该使用的方法句柄调用方式。
      进行类型匹配的基本规则是对比返回值类型和每个参数的类型是否都可以相互匹配。假设源类型为S,目标类型为T,则基本规则如下:
        1、可以通过java的类型转换来完成,一般从子类转成父类,比如从String到Object类型;
        2、可以通过基本类型的转换来完成,只能将类型范围的扩大,比如从int切换到long;
        3、可以通过基本类型的自动装箱和拆箱机制来完成,例如从int到Integer;
        4、如果S有返回值类型,而T的返回值类型为void,则S的返回值会被丢弃。
        5、如果S的返回值是void,而T的返回值是引用类型,T的返回值会是null;
        6、如果S的返回值是void,而T的返回值是基本类型,T的返回值会是0;
 
    第1、2、3条很好理解,第4、5、6条感觉原理都一样,我就用个新例子说明。
public class MethodHandleTest {
 
    public MethodHandle getHandler() {
        MethodHandle mh = null;
        MethodType mt = MethodType.methodType(void.class);
        MethodHandles.Lookup lk = MethodHandles.lookup();
         
        try {
            mh = lk.findVirtual(MethodHandleTest.class, "print", mt);
        } catch (Throwable e) {
            e.printStackTrace();
        }
         
        return mh;
    }
     
    public void print() {
        System.out.println("print");
    }
     
    public static void main(String[] args) throws Throwable {
        MethodHandleTest mht = new MethodHandleTest();
        MethodHandle mh = mht.getHandler();
         
        int result1 = (int) mh.invoke(mht);
        Object result2 = mh.invoke(mht);
         
        System.out.println("result 1:" + result1);
        System.out.println("result 2:" + result2);
    }
}

 

程序输出结果是:
print
print
result 1:0
result 2:null
 
 
参考资料:《java程序员修炼之道》、《深入理解java7核心技术与最佳实践》

转载于:https://www.cnblogs.com/night-wind/p/4405564.html

你可能感兴趣的:(MethodHandle(方法句柄)系列之三:invoke和invokeExact的区别)