先把代碼貼上來,用的是一樣的代碼
/** * * @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核心技術與最佳實踐》