Method Handles
Method Hanldes是在Java 7引入的概念姻成。全限定名是java.lang.invoke.MethodHandles腋逆。在這篇文章中聂沙,我們將學(xué)會(huì)如何創(chuàng)建内颗,使用MethodHandles及它的原理袖迎。
1.介紹
Method Handles的引入是為了與已經(jīng)存在的java.lang.reflect API相配合姻报。他們分別是為了解決不同的問(wèn)題而出現(xiàn)的瞒滴。從性能角度上說(shuō)鹤耍,MethodHandle api要比反射快很多因?yàn)樵L問(wèn)檢查在創(chuàng)建的時(shí)候就已經(jīng)完成了,而不是像反射一樣等到運(yùn)行時(shí)候才檢查苟穆。但同時(shí)抄课,Method Handles比反射更難用,因?yàn)闆](méi)有列舉類中成員雳旅,獲取屬性訪問(wèn)標(biāo)志之類的機(jī)制跟磨。
另外,MethodHandles可以操作方法攒盈,更改方法參數(shù)的類型和他們的順序吱晒。而反射則沒(méi)有這些功能。
從以上角度看沦童,反射更通用仑濒,但是安全性更差,因?yàn)榭梢栽诓皇跈?quán)的情況下使用反射對(duì)象偷遗。而method Handles遵從了分享者的能力墩瞳。所以method handle是一種更低級(jí)的發(fā)現(xiàn),適配和調(diào)用方法的方式氏豌,唯一的優(yōu)點(diǎn)就是更快喉酌。所以反射更適合主流Java開(kāi)發(fā)者,而method handle更適用于對(duì)編譯和運(yùn)行性能有要求的人。
2.使用
1.要使用method handle泪电,首先需要得到Lookup般妙。這是創(chuàng)造方法,構(gòu)造函數(shù)相速,屬性的method handles的工廠類碟渺。
// public方法的Lookup
MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();
// 所有方法的Lookup
MethodHandles.Lookup lookup = MethodHandles.lookup();
2.要?jiǎng)?chuàng)建MethodHandle,lookup需要一個(gè)定義了它的類型的MethodType對(duì)象突诬。這里的類型包括了傳入?yún)?shù)的類型苫拍,和最后返回的類型,要一一對(duì)應(yīng)旺隙。第一個(gè)是返回類型绒极,如果沒(méi)有返回值就是Void.class, 后面是可變的傳入?yún)?shù)的類型。
a MethodType represents the arguments and return type accepted and returned by a method handle or passed and expected by a method handle caller.
例如
// 接收數(shù)組蔬捷,返回一個(gè)List對(duì)象
MethodType mt = MethodType.methodType(List.class, Object[].class);
3.查找MethodHandle
Lookup之所以叫Lookup自然是因?yàn)樗麄冇胁檎襇ethodHandle的能力垄提。先看看他的方法。
4.接下來(lái)就可以進(jìn)行查找并調(diào)用了
MethodType mt = MethodType.methodType(String.class, char.class, char.class);
MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt);
String output = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');
5.方法調(diào)用細(xì)則:
有三種方法可以調(diào)用方法invoke(), invokeWithArugments()和invokeExact()周拐,當(dāng)我們使用invoke時(shí)铡俐,我們必須固定arguments的數(shù)目。
// invoke使用
MethodType mt = MethodType.methodType(String.class, char.class, char.class);
MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt);
String output = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');
// invokeWithArguments使用
MethodType mt = MethodType.methodType(List.class, Object[].class);
MethodHandle asList = publicLookup.findStatic(Arrays.class, "asList", mt);
List<Integer> list = (List<Integer>) asList.invokeWithArguments(1,2);
// invokeExact
MethodType mt = MethodType.methodType(int.class, int.class, int.class);
MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt);
int sum = (int) sumMH.invokeExact(1, 11);
具體的區(qū)別是:
與invokeExact方法不同速妖,invoke方法允許更加松散的調(diào)用方式。它會(huì)嘗試在調(diào)用的時(shí)候進(jìn)行返回值和參數(shù)類型的轉(zhuǎn)換工作聪黎。這是通過(guò)MethodHandle類的asType方法來(lái)完成的罕容,asType方法的作用是把當(dāng)前方法句柄適配到新的MethodType上面,并產(chǎn)生一個(gè)新的方法句柄稿饰。當(dāng)方法句柄在調(diào)用時(shí)的類型與其聲明的類型完全一致的時(shí)候锦秒,調(diào)用invoke方法等于調(diào)用invokeExact方法;否則喉镰,invoke方法會(huì)先調(diào)用asType方法來(lái)嘗試適配到調(diào)用時(shí)的類型旅择。如果適配成功,則可以繼續(xù)調(diào)用侣姆。否則會(huì)拋出相關(guān)的異常生真。這種靈活的適配機(jī)制,使invoke方法成為在絕大多數(shù)情況下都應(yīng)該使用的方法句柄調(diào)用方式捺宗。
參考:
https://www.baeldung.com/java-method-handles
https://www.cnblogs.com/night-wind/p/4405564.html