先把代码贴上来,用的是一样的代码
/** * * @author LiuYeFeng<897908343@qq.com> * @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,
1
,
3
);
中的(String)去掉类型转换的话,在调用的时候该方法会认为返回值是Object类型而不是String类型,然后系统报错。
与invokeExact方法不同,invoke方法允许更加松散的调用方式。它会尝试在调用的时候进行返回值和参数类型的转换工作。这是通过MethodHandle类的asType方法来完成的,asType方法的作用是把当前方法句柄适配到新的MethodType上面,并产生一个新的方法句柄。当方法句柄在调用时的类型与其声明的类型完全一致的时候,调用invoke方法等于调用invokeExact方法;否则,invoke方法会先调用asType方法来尝试适配到调用时的类型。如果适配成功,则可以继续调用。否则会抛出相关的异常。这种灵活的适配机制,使invoke方法成为在绝大多数情况下都应该使用的方法句柄调用方式。
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核心技术与最佳实践》