二話不說,上代碼
/** * * @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); } }
代碼輸出結果均為el。
接下來說一下方法句柄的調用過程,首先,在獲取方法句柄之前,先通過MethodType的靜態工廠方法,先生成一個包含方法參數類型、方法返回類型的的方法類型,也就是
MethodType mt = MethodType.methodType(String.
class
,
int
.
class
,
int
.
class
)。
其次,獲取方法句柄要用到Lookup對象,比如代碼中的MethodHandles.Lookup lk,這個對象可以提供其所在環境中任何可見方法的方法句柄。我們可以將其比喻成包含有某個類對象的方法成員、方法的容器,通過lk.findVirtual(String.
class
,
"substring"
, mt);
具體鎖定String類型中的某個方法,作為方法句柄返回。要從lookup對象中得到方法句柄,需要給出持有所需方法的類,方法的名稱,以及跟方法相匹配的方法類型。
最后,獲取到方法句柄后,我們就可以通過方法句柄來調用底層的方法,這點上,跟反射中的方法調用類似。方法句柄提供兩個方法調用底層方法,invoke和invokeExact方法。invokeExact方法與直接調用底層方法是一樣的,比如代碼中,mh.invoke(str, 1, 2)就相當於直接調用str.substring(1, 3);具體兩個方法的區別下面再說。這里先解析一下這兩個方法的調用,方法的調用參數基本都一樣,第一個參數為方法的接受對象,即是哪個對象執行這個方法,接下來的參數就是執行方法所需要的參數。當然第一個參數,也就是方法的接受對象,可以通過方法句柄的bindto動態的參數綁定方法來綁定,從而使方法句柄的調用和普通方法調用沒區別,動態的參數綁定我們以后再研究。
這里需要強調一下,靜態方法和動態方法之間的差別,靜態方法是不需要制定方法的接受對象的,而一般方法是需要的。
參考資料:《java程序員修煉之道》、《深入理解java7核心技術與最佳實踐》