從《Java虛擬機(jī)規(guī)范》中invokedynamic的描述可知invokedynamic的底層實(shí)現(xiàn)是基于java.lang.invoke.MethodHandle的,下面來(lái)詳細(xì)探討下MethodHandle的用法以及同Java Reflect的差異對(duì)比饥悴。
編程語(yǔ)言類(lèi)型
1竿秆、解釋型與編譯型語(yǔ)言
解釋型語(yǔ)言:是指通過(guò)解釋器解釋執(zhí)行的語(yǔ)言病袄,解釋型語(yǔ)言不需要編譯成與底層硬件平臺(tái)直接交互的機(jī)器碼語(yǔ)言轻腺,只要能被解釋器識(shí)別即可劫哼,可以是源碼或者類(lèi)似于字節(jié)碼的中間代碼,典型的如JavaScript样眠,Python友瘤,對(duì)應(yīng)的解釋器是谷歌V8 JavaScript執(zhí)行引擎引入的解釋器 Ignition,官方Python解釋器CPython檐束。
編譯型語(yǔ)言:是指可以通過(guò)編譯器編譯變成可以直接在底層硬件平臺(tái)上直接運(yùn)行的機(jī)器碼辫秧,可以與底層硬件直接交互的語(yǔ)言,典型的如C/C++厢塘,C-object茶没,操作系統(tǒng)如Linux,windows等晚碾,大小硬件設(shè)備的驅(qū)動(dòng)程序基本都是用C寫(xiě)的抓半。
-
解釋型語(yǔ)言因?yàn)楸仨毥?jīng)過(guò)中間的解釋器解釋執(zhí)行,所以性能相對(duì)而言要差格嘁,但是語(yǔ)法規(guī)則可以更靈活笛求,且因?yàn)榻忉屍髌帘瘟送讓佑布换サ募?xì)節(jié),解釋型語(yǔ)言學(xué)習(xí)成本更低糕簿,跨平臺(tái)能力更強(qiáng)探入。編譯型語(yǔ)言必須與底層硬件平臺(tái)直接交互,能夠基于底層平臺(tái)特性做特定優(yōu)化懂诗,所以性能更好蜂嗽,也是因此需要綜合考慮并兼顧不同底層硬件平臺(tái)的特性,代碼更加復(fù)雜殃恒,學(xué)習(xí)成本高植旧。
Java可以通過(guò)默認(rèn)的字節(jié)碼模板解釋器解釋執(zhí)行,也可以通過(guò)JIT即時(shí)編譯器編譯執(zhí)行离唐,通過(guò)-X+int選項(xiàng)指定以解釋方式執(zhí)行病附,通過(guò)-Xcomp選項(xiàng)指定以編譯方式執(zhí)行,通過(guò)-Xmixed亥鬓,以解釋+熱點(diǎn)代碼編譯的方式執(zhí)行完沪,默認(rèn)是-Xmixed。
2嵌戈、靜態(tài)類(lèi)型語(yǔ)言與動(dòng)態(tài)類(lèi)型語(yǔ)言
靜態(tài)類(lèi)型語(yǔ)言:是指對(duì)數(shù)據(jù)類(lèi)型/方法調(diào)用的檢查是在編譯期執(zhí)行的覆积,根據(jù)變量聲明的類(lèi)型來(lái)檢查,典型的如Java
動(dòng)態(tài)類(lèi)型語(yǔ)言:是指對(duì)數(shù)據(jù)類(lèi)型/方法調(diào)用的檢查是在運(yùn)行期執(zhí)行的熟呛,根據(jù)變量實(shí)際的值類(lèi)型來(lái)檢查技健,典型的如JavaScript
Java的測(cè)試用例如下:
public class TypeTest {
public static void test(String s){
System.out.println("test:"+s);
}
public static void main(String[] args) {
int a=12;
TypeTest.test(a);
}
}
執(zhí)行javac編譯報(bào)錯(cuò),如下圖:
如果是方法調(diào)用如TypeTest.test(a);會(huì)檢查T(mén)ypeTest類(lèi)是否包含靜態(tài)test方法惰拱。
JavaScript的測(cè)試用例如下:
function add(a,b){
return a+b;
}
console.log(add(1,2));
console.log(add("a",2));
執(zhí)行結(jié)果如下:
聲明add方法時(shí)不需要聲明變量a和變量b的類(lèi)型,沒(méi)有聲明肯定就不會(huì)在編譯時(shí)校驗(yàn)類(lèi)型了,解釋執(zhí)行的時(shí)候會(huì)根據(jù)變量a和變量b的實(shí)際類(lèi)型決定a+b的結(jié)果偿短,如果兩個(gè)都是數(shù)字則a+b表示兩個(gè)數(shù)字相加欣孤,如果其中一個(gè)表示字符串則a+b表示將a和b以字串符的形式連接起來(lái)。
3昔逗、強(qiáng)類(lèi)型語(yǔ)言和弱類(lèi)型語(yǔ)言
強(qiáng)類(lèi)型語(yǔ)言是指一個(gè)變量如果被聲明成什么類(lèi)型了降传,在整個(gè)運(yùn)行期該變量就一直是該類(lèi)型,除非被強(qiáng)轉(zhuǎn)成其他數(shù)據(jù)類(lèi)型勾怒,典型的如Java
弱類(lèi)型語(yǔ)言是指一個(gè)變量在聲明時(shí)不需要指定特定的類(lèi)型婆排,而是可以在運(yùn)行時(shí)賦值成任一數(shù)據(jù)類(lèi)型的值,典型的如JavaScipt笔链。
Java的測(cè)試用例如下:
public class TypeTest {
public static void main(String[] args) {
int a=12;
long b=13L;
a=b;
}
}
javac編譯報(bào)錯(cuò):
上述代碼如果改成a=(int)b就不會(huì)報(bào)錯(cuò)段只,即將b強(qiáng)轉(zhuǎn)成int賦值給a,b的類(lèi)型依然是long鉴扫。
javaScript的測(cè)試用例如下:
var a=12;
console.log(a);
a="test";
console.log(a);
執(zhí)行結(jié)果如下:
變量a先是被賦值成整數(shù)類(lèi)型赞枕,接著又被賦值成字符串類(lèi)型,都可以正常執(zhí)行坪创。
4炕婶、動(dòng)態(tài)語(yǔ)言和靜態(tài)語(yǔ)言
動(dòng)態(tài)語(yǔ)言:指在運(yùn)行時(shí)可以改變其結(jié)構(gòu)的語(yǔ)言,如新的屬性莱预、函數(shù)柠掂、對(duì)象,甚至代碼可以被引進(jìn)依沮,已有的函數(shù)可以被刪除或是其他結(jié)構(gòu)上的變化涯贞,典型的如JavaScript
靜態(tài)語(yǔ)言:指運(yùn)行時(shí)結(jié)構(gòu)不可變的語(yǔ)言,典型的如Java
JavaScript的測(cè)試用例如下:
class cla{
};
cla.a=1;
console.log(cla.a);
cla.test=function(){console.log("test");};
cla.test();
代碼執(zhí)行結(jié)果如下:
a和test不屬于cla中定義的屬性悉抵,而是在運(yùn)行時(shí)動(dòng)態(tài)添加的
Java測(cè)試用例如下:
public class TypeTest {
public int a;
public static void main(String[] args) {
TypeTest typeTest=new TypeTest();
typeTest.a=1;
typeTest.b=2;
}
}
javac編譯報(bào)錯(cuò)肩狂,如下圖:
屬性a是TypeTest中聲明的所以typeTest.a=1;沒(méi)有報(bào)錯(cuò),屬性b不是TypeTest中聲明的姥饰,所以報(bào)錯(cuò)找不到符號(hào)傻谁。
java.lang.invoke
1、JSR-292
java.lang.invoke是Java7實(shí)現(xiàn)JSR-292而引入的列粪,為了在缺乏靜態(tài)類(lèi)型信息审磁,運(yùn)行時(shí)才能獲取類(lèi)型信息的情況下能夠高效和靈活的執(zhí)行方法調(diào)用,即在方法調(diào)用層面上提供對(duì)動(dòng)態(tài)類(lèi)型語(yǔ)言的支持岂座,如執(zhí)行obj.println("hello world"); 不再要求obj必須是java.io.PrintStream類(lèi)型态蒂,只要在運(yùn)行期obj定義了println方法即可。
因?yàn)橹岸x的四個(gè)方法調(diào)用指令invokevirtual invokespecial invokestatic invokeinterface的第一個(gè)參數(shù)都是被調(diào)用方法的符號(hào)引用费什,根據(jù)符號(hào)引用可以解析出被調(diào)用方法的接收對(duì)象的類(lèi)型和方法定義钾恢,即這四個(gè)指令要求在編譯期必須確認(rèn)被調(diào)用方法的接收對(duì)象的對(duì)象類(lèi)型,如編譯obj.println("hello world"); 必須確認(rèn)obj的具體的類(lèi)型,然后檢查該類(lèi)型是否定義了println方法瘩蚪,檢查通過(guò)再據(jù)此生成一個(gè)被調(diào)用方法的符號(hào)引用泉懦。為了能夠在JVM字節(jié)碼指令層面實(shí)現(xiàn)在運(yùn)行期才確認(rèn)被調(diào)用方法的接收對(duì)象的類(lèi)型,JVM配套的引入了一個(gè)新的方法調(diào)用指令invokedynamic疹瘦,該指令同時(shí)也是Java8實(shí)現(xiàn)lamada的技術(shù)基礎(chǔ)崩哩。注意java.lang.invoke包的底層實(shí)現(xiàn)并沒(méi)有依賴(lài)invokedynamic指令,在非lamada下javac目前也不會(huì)生成invokedynamic指令言沐,invokedynamic指令主要給同樣是JVM上運(yùn)行的動(dòng)態(tài)類(lèi)型語(yǔ)言使用邓嘹,如Groovy。
測(cè)試用例如下:
package jni;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.List;
class TestA{
public void println(String s){
System.out.println("TestA println:"+s);
}
}
class TestB{
public void println(String s){
System.out.println("TestB println:"+s);
}
}
public class MethodHandlerTest {
public static void lamadaTest(){
List<String> s= Arrays.asList("a","b","c");
s.stream().forEach((String str) ->{System.out.println(str);});
}
public static void println(Object obj,String s) throws Throwable{
MethodType mt=MethodType.methodType(void.class,String.class);
MethodHandle methodHandle=MethodHandles.lookup().findVirtual(obj.getClass(),"println",mt);
methodHandle.bindTo(obj).invokeExact(s);
}
public static void main(String[] args) throws Throwable{
String test="Hello World";
println(new TestA(),test);
println(new TestB(),test);
println(System.out,test);
lamadaTest();
}
}
上述用例中執(zhí)行println方法時(shí)险胰,obj的具體類(lèi)型是不確定的汹押,是在運(yùn)行時(shí)才確認(rèn)的,執(zhí)行結(jié)果如下:
通過(guò)javap -v可以查看MethodHandlerTest的字節(jié)碼鸯乃,如下:
public static void lamadaTest();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=0
0: iconst_3
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String a
8: aastore
9: dup
10: iconst_1
11: ldc #4 // String b
13: aastore
14: dup
15: iconst_2
16: ldc #5 // String c
18: aastore
19: invokestatic #6 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
22: astore_0
23: aload_0
24: invokeinterface #7, 1 // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
29: invokedynamic #8, 0 // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;
34: invokeinterface #9, 2 // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
39: return
LineNumberTable:
line 24: 0
line 25: 23
line 26: 39
LocalVariableTable:
Start Length Slot Name Signature
23 17 0 s Ljava/util/List;
LocalVariableTypeTable:
Start Length Slot Name Signature
23 17 0 s Ljava/util/List<Ljava/lang/String;>;
public static void println(java.lang.Object, java.lang.String) throws java.lang.Throwable;
descriptor: (Ljava/lang/Object;Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=4, args_size=2
0: getstatic #2 // Field java/lang/Void.TYPE:Ljava/lang/Class;
3: ldc #3 // class java/lang/String
5: invokestatic #4 // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
8: astore_2
9: invokestatic #5 // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
12: aload_0
13: invokevirtual #6 // Method java/lang/Object.getClass:()Ljava/lang/Class;
16: ldc #7 // String println
18: aload_2
19: invokevirtual #8 // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
22: astore_3
23: aload_3
24: aload_0
25: invokevirtual #9 // Method java/lang/invoke/MethodHandle.bindTo:(Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
28: aload_1
29: invokevirtual #10 // Method java/lang/invoke/MethodHandle.invokeExact:(Ljava/lang/String;)V
32: return
LineNumberTable:
line 22: 0
line 23: 9
line 24: 23
line 25: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 obj Ljava/lang/Object;
0 33 1 s Ljava/lang/String;
9 24 2 mt Ljava/lang/invoke/MethodType;
23 10 3 methodHandle Ljava/lang/invoke/MethodHandle;
Exceptions:
throws java.lang.Throwable
public static void main(java.lang.String[]) throws java.lang.Throwable;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: ldc #18 // String Hello World
2: astore_1
3: new #19 // class jni/TestA
6: dup
7: invokespecial #20 // Method jni/TestA."<init>":()V
10: aload_1
11: invokestatic #21 // Method println:(Ljava/lang/Object;Ljava/lang/String;)V
14: new #22 // class jni/TestB
17: dup
18: invokespecial #23 // Method jni/TestB."<init>":()V
21: aload_1
22: invokestatic #21 // Method println:(Ljava/lang/Object;Ljava/lang/String;)V
25: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_1
29: invokestatic #21 // Method println:(Ljava/lang/Object;Ljava/lang/String;)V
32: invokestatic #25 // Method lamadaTest:()V
35: return
LineNumberTable:
line 35: 0
line 36: 3
line 37: 14
line 38: 25
line 40: 32
line 41: 35
LocalVariableTable:
Start Length Slot Name Signature
0 36 0 args [Ljava/lang/String;
3 33 1 test Ljava/lang/String;
Exceptions:
throws java.lang.Throwable
從上述字節(jié)碼可知java.lang.invoke包的相關(guān)方法調(diào)用并沒(méi)有使用invokedynamic指令鲸阻,lamada語(yǔ)句使用了invokedynamic指令。
2缨睡、MethodType
MethodType用來(lái)表示方法的入?yún)㈩?lèi)型和返回值類(lèi)型鸟悴,相當(dāng)于期望的被調(diào)用方法的方法描述符,在執(zhí)行MethodHandle#invokeExact方法奖年、MethodHandle#invoke方法或者invokedynamic指令時(shí)细诸,JVM會(huì)檢查被調(diào)用方法與MethodType是否匹配。
一個(gè)MethodType由一個(gè)返回類(lèi)型和任意多個(gè)參數(shù)類(lèi)型組成陋守,每個(gè)類(lèi)型都是Class震贵,如基本類(lèi)型int.class,對(duì)象類(lèi)型String.class水评,無(wú)返回值void.class猩系。
所有的MethodType實(shí)例同String實(shí)例一樣都是不可變的,當(dāng)兩個(gè)MethodType實(shí)例的返回類(lèi)型和參數(shù)類(lèi)型都相同則認(rèn)為它們相等中燥。MethodType只能通過(guò)工廠(chǎng)方法創(chuàng)建寇甸,傳遞參數(shù)類(lèi)型時(shí)可以通過(guò)數(shù)組或者Listc傳遞。
MethodType可以通過(guò)方法描述符創(chuàng)建疗涉,這時(shí)方法描述符中涉及的類(lèi)就必須已經(jīng)完成加載拿霉,但是可以不初始化。
MethodType提供的方法可以分為以下幾類(lèi):
1)工廠(chǎng)方法
其中g(shù)enericMethodType方法是針對(duì)方法的返回值和參數(shù)類(lèi)型都是Object時(shí)的methodType(java.lang.Class, java.lang.Class[])的簡(jiǎn)便方法咱扣。
2)修改返回參數(shù)類(lèi)型和方法參數(shù)類(lèi)型的方法
因?yàn)镸ethodType是不可變的绽淘,所以上述方法實(shí)際是在當(dāng)前MethodType的基礎(chǔ)上做適當(dāng)修改然后構(gòu)造了一個(gè)新的MethodType實(shí)例。
3)讀取返回參數(shù)類(lèi)型和方法參數(shù)類(lèi)型的方法
其中 hasPrimitives方法判斷MethodType的參數(shù)類(lèi)型和返回類(lèi)型中是否包含基本類(lèi)型闹伪,hasWrappers判斷是否包含包裝器類(lèi)型沪铭,如Interger壮池。
4)對(duì)當(dāng)前的MethodType進(jìn)行轉(zhuǎn)換的便利方法
erase方法將MethodType的參數(shù)類(lèi)型和返回類(lèi)型中包含的引用類(lèi)型全部替換成Object.class
generic方法將MethodType的參數(shù)類(lèi)型和返回類(lèi)型中包含的所有類(lèi)型全部替換成Object.class
wrap()方法將MethodType的參數(shù)類(lèi)型和返回類(lèi)型中包含的基本類(lèi)型全部替換成對(duì)應(yīng)的包裝器類(lèi)型
unwrap()方法將MethodType的參數(shù)類(lèi)型和返回類(lèi)型中包含的包裝器類(lèi)型全部替換成對(duì)應(yīng)的基本類(lèi)型
5)跟方法描述符字符串來(lái)回轉(zhuǎn)換的方法:
3、MethodHandles.Lookup
Lookup類(lèi)是MethodHandles的內(nèi)部類(lèi)伦意,用public static final 修飾火窒。Lookup就是一個(gè)創(chuàng)建MethodHandler的工廠(chǎng)類(lèi),Lookup在只在創(chuàng)建MethodHandler時(shí)檢查執(zhí)行l(wèi)ookup的類(lèi)對(duì)目標(biāo)方法的訪(fǎng)問(wèn)權(quán)限驮肉,而不是在通過(guò)MethodHandler調(diào)用特定方法時(shí)檢查,Java反射API會(huì)每次調(diào)用都需要檢查權(quán)限已骇,這是Lookup與Java反射API最關(guān)鍵的不同离钝。執(zhí)行MethodHandles#lookup方法的類(lèi)的Class作為構(gòu)造方法參數(shù)構(gòu)造Lookup實(shí)例,可以通過(guò)lookupClass()方法獲取該Class褪储,稱(chēng)之為lookup class卵渴。構(gòu)造的Lookup實(shí)例也可以被共享,其他代碼使用共享的Lookup實(shí)例時(shí)鲤竹,依然使用創(chuàng)建Lookup實(shí)例時(shí)的類(lèi)的Class來(lái)檢查訪(fǎng)問(wèn)權(quán)限而非當(dāng)前使用Lookup實(shí)例的類(lèi)的Class浪读。
Lookup類(lèi)提供了多個(gè)用于創(chuàng)建操作構(gòu)造方法,普通方法和字段的的MethodHandle的工廠(chǎng)方法辛藻,這些方法創(chuàng)建的MethodHandle的底層操作和對(duì)應(yīng)字節(jié)碼的底層操作是基本等同的,如下表碘橘。少數(shù)情況下不等同,參考MethodHandles.Lookup的注釋說(shuō)明吱肌。
表中C表示執(zhí)行l(wèi)ookup的目標(biāo)類(lèi)痘拆,F(xiàn)T表示目標(biāo)字段的類(lèi)型,MT表示目標(biāo)方法的MethodType氮墨,aMethod纺蛆,aField,aConstructor表示反射中的Method规揪,F(xiàn)ield桥氏,Constructor實(shí)例;表中最后一個(gè)unreflect方法應(yīng)該是unreflectSpecial猛铅,官方注釋有誤字支。比如執(zhí)行Lookup#findGetter方法獲取MethodHandle,通過(guò)該MethodHandle獲取某個(gè)類(lèi)FT的屬性f奕坟,其底層操作上相當(dāng)于this.f對(duì)應(yīng)的字節(jié)碼getfiled祥款。表中的findSpecial方法需要重點(diǎn)關(guān)注,findSpecial方法要求最后一個(gè)參數(shù)Class<?> specialCaller與lookup 實(shí)例保存的lookup class一樣且lookup實(shí)例保存的lookup class具有訪(fǎng)問(wèn)第一個(gè)參數(shù)Class<?> refc類(lèi)的私有成員的權(quán)限月杉,這樣做的目的是將lookup能夠訪(fǎng)問(wèn)的私有成員限制在lookup class能夠訪(fǎng)問(wèn)的私有成員范圍內(nèi)刃跛。
除上述方法外,Lookup類(lèi)還包含如下方法:
- int lookupModes():獲取Lookup實(shí)例能夠訪(fǎng)問(wèn)的成員類(lèi)型苛萎,其結(jié)果是取Lookup實(shí)例的allowedModes屬性同PACKAGE (0x08)的并運(yùn)算桨昙,allowedModes只能是PUBLIC (0x01)检号, PRIVATE (0x02), PROTECTED (0x04)蛙酪,ALL_MODES
四個(gè)掩碼之一齐苛。 - MethodHandles.Lookup in(Class<?> requestedLookupClass):用一個(gè)新的lookupClass創(chuàng)建一個(gè)新的Lookup實(shí)例,但是新實(shí)例能夠訪(fǎng)問(wèn)的成員比原來(lái)的少桂塞。
- MethodHandle bind(Object receiver,String name,MethodType type):從receiver對(duì)應(yīng)的Class中查找目標(biāo)方法凹蜂,如果存在且當(dāng)前Lookup實(shí)例通過(guò)findVirtual可以訪(fǎng)問(wèn)該方法,則將receiver綁定到Lookup實(shí)例創(chuàng)建的MethodHandle上阁危。
測(cè)試用例如下:
import java.lang.invoke.MethodHandles;
interface interfaceTest{
void interTest();
}
class superA{
public void println(String s){
System.out.println("superA println:"+s);
}
}
public class TestB extends superA implements interfaceTest{
public int a;
public static int b;
private int c;
public TestB(){
this.a=11;
b=12;
c=13;
};
TestB(int a,String s){
System.out.println("a="+a+",s="+s);
this.a=21;
b=22;
c=23;
}
public void println(String s){
System.out.println("TestB println:"+s);
}
private void privateTest(String s){
System.out.println("TestB privateTest:"+s);
}
public static void staticPrintln(String s){
System.out.println("TestB staticPrintln:"+s);
}
public static MethodHandles.Lookup lookup(){
return MethodHandles.lookup();
}
@Override
public void interTest() {
System.out.println("TestB interTest");
}
@Override
public String toString() {
return "TestB{" +
"a=" + a +
",b=" + b +
",c=" + c +
'}';
}
}
import org.junit.Test;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class LookupTest{
@Test
public void testField() throws Throwable {
TestB testB=new TestB();
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle setter=lookup.findSetter(TestB.class,"a",int.class);
setter.bindTo(testB).invoke(2);
MethodHandle staticSetter=lookup.findStaticSetter(TestB.class,"b",int.class);
staticSetter.invoke(3);
System.out.println("setter result->"+testB);
MethodHandle getter=lookup.findGetter(TestB.class,"a",int.class);
int a=(int)getter.bindTo(testB).invoke();
MethodHandle staticGetter=lookup.findStaticGetter(TestB.class,"b",int.class);
int b=(int)staticGetter.invoke();
System.out.println("getter result TestB a="+a+",b="+b);
Field aField=TestB.class.getField("a");
aField.setInt(testB,4);
Field bField=TestB.class.getField("b");
bField.setInt(null,5);
System.out.println("reflect setter result->"+testB);
a=aField.getInt(testB);
b=bField.getInt(null);
System.out.println("reflect getter result TestB a="+a+",b="+b);
//無(wú)法訪(fǎng)問(wèn)私有屬性玛痊,報(bào)錯(cuò)member is private: TestB.c/int/putField, from MethodHandleTest
// MethodHandle privateSetter=lookup.findSetter(TestB.class,"c",int.class);
//返回的lookup實(shí)例的lookup class就是TestB.class,所以可以訪(fǎng)問(wèn)私有成員
lookup=TestB.lookup();
MethodHandle privateSetter=lookup.findSetter(TestB.class,"c",int.class);
privateSetter.bindTo(testB).invoke(6);
System.out.println("findSetter private->"+testB);
//無(wú)法訪(fǎng)問(wèn)私有屬性狂打,報(bào)錯(cuò)java.lang.NoSuc
// Field cField=TestB.class.getField("c");
Field cField=TestB.class.getDeclaredField("c");
//必須執(zhí)行setAccessible(true); 否則報(bào)錯(cuò)Class MethodHandleTest can not access a member of class TestB with modifiers "private"
cField.setAccessible(true);
cField.setInt(testB,7);
System.out.println(testB);
int c=cField.getInt(testB);
System.out.println("private get c="+c);
}
@Test
public void constuctTest() throws Throwable{
MethodHandles.Lookup lookup=MethodHandles.lookup();
//構(gòu)造函數(shù)的返回類(lèi)型是void
MethodHandle defaultConstructor=lookup.findConstructor(TestB.class, MethodType.methodType(void.class));
TestB testB=(TestB) defaultConstructor.invoke();
System.out.println("defaultConstructor:"+testB);
MethodHandle paramConstructor=lookup.findConstructor(TestB.class, MethodType.methodType(void.class,int.class,String.class));
testB=(TestB) paramConstructor.invoke(1,"test");
System.out.println("param constructor:"+testB);
Constructor defaultConstructor2=TestB.class.getConstructor();
testB=(TestB) defaultConstructor2.newInstance();
System.out.println("reflect defaultConstructor:"+testB);
testB=TestB.class.newInstance();
System.out.println("reflect newInstance:"+testB);
//getConstructor是查找公開(kāi)的構(gòu)造方法擂煞,此構(gòu)造方法是默認(rèn)的包級(jí)訪(fǎng)問(wèn),所以getConstructor報(bào)錯(cuò)
// Constructor paramConstructor2=TestB.class.getConstructor(int.class,String.class);
//getDeclaredConstructor是獲取聲明的構(gòu)造方法趴乡,獲取的Constructor執(zhí)行newInstance方法時(shí)會(huì)校驗(yàn)當(dāng)前類(lèi)是否
//具備訪(fǎng)問(wèn)權(quán)限对省,如果沒(méi)有權(quán)限需要執(zhí)行setAccessible(true);
Constructor paramConstructor2=TestB.class.getDeclaredConstructor(int.class,String.class);
testB=(TestB) paramConstructor2.newInstance(7,"ts");
System.out.println("reflect paramConstructor2:"+testB);
}
@Test
public void methodTest() throws Throwable {
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle staticHandle=lookup.findStatic(TestB.class,"staticPrintln",MethodType.methodType(void.class,String.class));
staticHandle.invoke("static test");
//方法沒(méi)有入?yún)ⅲ瑒t不用傳任何類(lèi)型
MethodHandle interfaceHandle=lookup.findVirtual(interfaceTest.class,"interTest",MethodType.methodType(void.class));
interfaceHandle.bindTo(new TestB()).invoke();
MethodHandle virtualHandle=lookup.findVirtual(superA.class,"println",MethodType.methodType(void.class,String.class));
// 如果使用TestB.class晾捏,則執(zhí)行bindTo(new superA())時(shí)java.lang.ClassCastException: Cannot cast superA to TestB
// MethodHandle virtualHandle=lookup.findVirtual(TestB.class,"println",MethodType.methodType(void.class,String.class));
virtualHandle.bindTo(new superA()).invoke("super test");
virtualHandle.bindTo(new TestB()).invoke("sub test");
//findSpecial方法要求第四個(gè)參數(shù)specialCaller和lookup class一致或者lookup class可以訪(fǎng)問(wèn)私有成員蒿涎,即必須在目標(biāo)類(lèi)中創(chuàng)建lookup
//否則報(bào)錯(cuò)no private access for invokespecial: class TestB, from MethodHandleTest
//可以通過(guò)findSpecial調(diào)用私有方法,父類(lèi)方法
// MethodHandle specialHandle=lookup.findSpecial(TestB.class,"println",MethodType.methodType(void.class,String.class),TestB.class);
lookup=TestB.lookup();
MethodHandle specialHandle=lookup.findSpecial(TestB.class,"println",MethodType.methodType(void.class,String.class),TestB.class);
specialHandle.bindTo(new TestB()).invoke("findSpecial test");
specialHandle=lookup.findSpecial(superA.class,"println",MethodType.methodType(void.class,String.class),TestB.class);
specialHandle.bindTo(new TestB()).invoke("findSpecial test");
//調(diào)用私有方法
specialHandle=lookup.findSpecial(TestB.class,"privateTest",MethodType.methodType(void.class,String.class),TestB.class);
specialHandle.bindTo(new TestB()).invoke("findSpecial privateTest");
Method method=superA.class.getMethod("println",String.class);
//如果是TestB.class,則執(zhí)行invoke new superA()時(shí)報(bào)錯(cuò)object is not an instance of declaring class
// Method method=TestB.class.getMethod("println",String.class);
method.invoke(new superA(),"reflect super test");
method.invoke(new TestB(),"reflect sub test");
method=TestB.class.getDeclaredMethod("privateTest",String.class);
method.setAccessible(true);
method.invoke(new TestB(),"reflect private test");
}
@Test
public void reflectTest() throws Throwable {
MethodHandles.Lookup lookup=MethodHandles.lookup();
TestB testB=new TestB();
Field aField=TestB.class.getField("a");
MethodHandle setter=lookup.unreflectSetter(aField);
setter.bindTo(testB).invoke(22);
System.out.println(testB);
MethodHandle getter=lookup.unreflectGetter(aField);
int a=(int)getter.bindTo(testB).invoke();
System.out.println("testB a="+a);
Method method=superA.class.getMethod("println",String.class);
MethodHandle methodHandle=lookup.unreflect(method);
methodHandle.bindTo(testB).invoke("unreflect test");
//報(bào)錯(cuò)no private access for invokespecial: class TestB, from MethodHandleTest
// MethodHandle specialMethodHandle=lookup.unreflectSpecial(method,TestB.class);
MethodHandle specialMethodHandle=TestB.lookup().unreflectSpecial(method,TestB.class);
//調(diào)用TestB中println方法的父類(lèi)實(shí)現(xiàn)
specialMethodHandle.bindTo(testB).invoke("unreflectSpecial test");
Constructor paramConstructor=TestB.class.getDeclaredConstructor(int.class,String.class);
MethodHandle constuctorMethodHandle=lookup.unreflectConstructor(paramConstructor);
TestB testB2=(TestB) constuctorMethodHandle.invoke(33,"unreflectConstructor test");
System.out.println(testB2);
}
}
4粟瞬、MethodHandle
一個(gè)MethodHandle實(shí)例表示一個(gè)可以直接執(zhí)行的對(duì)普通方法同仆,構(gòu)造方法,字段等操作或者其他的低級(jí)別的操作的引用裙品。MethodHandle是跟方法的返回值類(lèi)型和參數(shù)類(lèi)型強(qiáng)相關(guān)的俗批,它們并不是根據(jù)方法名或者定義方法的類(lèi)來(lái)區(qū)分的,通過(guò)MethodHandle調(diào)用某個(gè)方法時(shí)要求目標(biāo)方法的描述符必須與MethodHandle的類(lèi)型描述符(即關(guān)聯(lián)的MethodType實(shí)例)一致市怎,可通過(guò)MethodHandle#type()方法獲取關(guān)聯(lián)的MethodType實(shí)例岁忘,MethodType實(shí)例決定了MethodHandler支持調(diào)用的方法類(lèi)型。
MethodHandle有兩個(gè)執(zhí)行具體方法調(diào)用的方法 invokeExact和invoke区匠,invokeExact要求被調(diào)用方法的描述符與MethodHandle完全一致干像,而invoke則允許適當(dāng)?shù)牟灰恢拢绻恢缕鋱?zhí)行速度和invokeExact一樣驰弄,如果不一致invoke會(huì)嘗試通過(guò)MethodHandle#asType(MethodType newType)方法創(chuàng)建一個(gè)跟目標(biāo)方法一致的MethodType麻汰,然后利用該MethodType執(zhí)行invokeExact方法。但是不能保證asType會(huì)被調(diào)用戚篙,如果JVM可以自動(dòng)適配目標(biāo)方法的參數(shù)就會(huì)直接調(diào)用了五鲫。
MethodHandle實(shí)例本身是不可變的,也沒(méi)有任何可見(jiàn)的狀態(tài)岔擂,相當(dāng)于一個(gè)final變量位喂。MethodHandle是一個(gè)抽象類(lèi)但是不建議開(kāi)發(fā)者實(shí)現(xiàn)一個(gè)子類(lèi)浪耘,因?yàn)镸ethodHandler的類(lèi)繼承體系未來(lái)可能隨著時(shí)間的改變而改變喇颁。
MethodHandle提供的方法可以分為以下幾類(lèi):
1)方法調(diào)用:
Object invokeExact(Object... args):要求執(zhí)行調(diào)用時(shí)的參數(shù)類(lèi)型與返回值類(lèi)型與MethodHandle關(guān)聯(lián)的MethodType完全一致
Object invoke(Object... args):跟invokeExact相比京闰,不要求完全一致老速,invoke會(huì)通過(guò)判斷執(zhí)行調(diào)用時(shí)的參數(shù)類(lèi)型與返回值類(lèi)型能否轉(zhuǎn)換成MethodHandle關(guān)聯(lián)的MethodType诵棵,如果能轉(zhuǎn)換則通過(guò)MethodHandle asType(MethodType newType)方法生成一個(gè)新的MethodHandle,對(duì)新的MethodHandle執(zhí)行invokeExact方法惶凝。
MethodHandle bindTo(Object x):將x作為invoke或者invokeExact的第一個(gè)參數(shù)瞒斩,即方法調(diào)用的接收對(duì)象淋纲,如果沒(méi)有執(zhí)行該方法聋呢,則invoke或者invokeExact將第一個(gè)參數(shù)作為方法調(diào)用的接收對(duì)象苗踪。
Object invokeWithArguments(Object... arguments) :執(zhí)行方法調(diào)用,要求方法參數(shù)是多個(gè)跟arguments個(gè)數(shù)一致的Object
Object invokeWithArguments(java.util.List<?> arguments)削锰,同上
2)MethodHandler轉(zhuǎn)換
MethodHandle asType(MethodType newType) :用新的MethodType 創(chuàng)建一個(gè)新的MethodHandle
MethodHandle asSpreader(Class<?> arrayType, int arrayLength): 支持將調(diào)用參數(shù)中的數(shù)組的元素轉(zhuǎn)換成方法入?yún)?br>
MethodHandle asCollector(Class<?> arrayType, int arrayLength): 將可變參數(shù)個(gè)數(shù)的MethodHandle轉(zhuǎn)換成固定參數(shù)個(gè)數(shù)的
MethodHandle asVarargsCollector(Class<?> arrayType):將數(shù)組形式的入?yún)⑥D(zhuǎn)換成支持可變參數(shù)個(gè)數(shù)的MethodHandle
MethodHandle asFixedArity() :將參數(shù)類(lèi)型固定,不允許invoke調(diào)用時(shí)做類(lèi)型轉(zhuǎn)換
測(cè)試用例如下:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.WrongMethodTypeException;
import java.util.Arrays;
import java.util.List;
import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;
import static org.junit.Assert.assertEquals;
public class MethodHandleTest {
@Test
public void invokeTest() throws Throwable {
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle methodHandle=lookup.findStatic(TestB.class,"staticPrintln", methodType(void.class,Object.class));
//invoke內(nèi)部會(huì)調(diào)用asType產(chǎn)生一個(gè)適配的MethodType毕莱,不過(guò)能夠適配的類(lèi)型非常有限
//能夠適配的邏輯參考MethodType#canConvert方法
methodHandle.invoke(122);
//invokeExact要求類(lèi)型嚴(yán)格匹配器贩,test是String不是Object類(lèi)型
// methodHandle.invokeExact("test");
MethodType newType=methodHandle.type().changeParameterType(0,String.class);
methodHandle=methodHandle.asType(newType);
methodHandle.invokeExact("test");
}
@Test
public void asVarargsCollectorTest() throws Throwable {
MethodHandles.Lookup lookup=MethodHandles.lookup();
//Arrays#deepToString方法本來(lái)的參數(shù)類(lèi)型是Object[],不是不可變參數(shù)類(lèi)型
MethodHandle methodHandle=lookup.findStatic(Arrays.class,"deepToString", methodType(String.class,Object[].class));
//返回false朋截,表示不支持可變參數(shù)
System.out.println(methodHandle.isVarargsCollector());
//調(diào)用報(bào)錯(cuò)cannot convert MethodHandle(Object[])String to (int,int,int,int)List
// String result=(String)methodHandle.invoke(1,2,3,4);
String result=(String)methodHandle.invoke(new Object[]{1,2,3,4});
System.out.println(result);
//asVarargsCollector將其轉(zhuǎn)換成支持可變參數(shù)個(gè)數(shù)的MethodHandle
//如果MethodHandle對(duì)應(yīng)的方法本身就支持可變參數(shù)則不做任何轉(zhuǎn)換
methodHandle=methodHandle.asVarargsCollector(Object[].class);
result=(String)methodHandle.invoke(1,2,3,4);
System.out.println(result);
result=(String)methodHandle.invoke(new Object[]{11,12,13,14});
System.out.println(result);
methodHandle=lookup.findStatic(Arrays.class,"asList", methodType(List.class,Object[].class));
System.out.println(methodHandle.isVarargsCollector());
List list=(List) methodHandle.invoke(21,22,23,24);
System.out.println(list);
}
@Test
public void asCollectorTest() throws Throwable {
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle methodHandle=lookup.findStatic(Arrays.class,"deepToString", methodType(String.class,Object[].class));
methodHandle=methodHandle.asCollector(Object[].class,3);
System.out.println(methodHandle.type().equals(methodType(String.class,Object.class,Object.class,Object.class)));
//將原來(lái)的Object數(shù)組變成指定參數(shù)個(gè)數(shù)蛹稍,如果個(gè)數(shù)不符報(bào)錯(cuò)cannot convert MethodHandle(Object,Object,Object)String to (String,String)String
// String result=(String)methodHandle.invoke("a","b");
// new Object[]被當(dāng)成一個(gè)對(duì)象,報(bào)錯(cuò)cannot convert MethodHandle(Object,Object,Object)String to (Object[])String
// String result=(String)methodHandle.invoke(new Object[]{"a","b","c"});
String result=(String)methodHandle.invoke("a","b","c");
System.out.println(result);
System.out.println(methodHandle.isVarargsCollector());
}
@Test
public void asSpreaderTest() throws Throwable {
MethodHandle equals = publicLookup()
.findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
assert((boolean)equals.bindTo("me").invokeExact((Object) "me"));
assert(!(boolean)equals.bindTo("me").invokeExact((Object)"thee"));
assert( (boolean) equals.invokeExact("me", (Object)"me"));
assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
//toString方法的參數(shù)類(lèi)型都是數(shù)組
MethodHandle caToString = publicLookup()
.findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
//限定方法的參數(shù)個(gè)數(shù)是3
MethodHandle caString3 = caToString.asCollector(char[].class, 3);
assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
//會(huì)把數(shù)組元素轉(zhuǎn)換成3個(gè)參數(shù)
MethodHandle caToString3 = caString3.asSpreader(char[].class, 3);
assertEquals("[A, B, C]", (String) caToString3.invokeExact("ABC".toCharArray()));
//報(bào)錯(cuò)數(shù)組長(zhǎng)度不對(duì) array is not of length 3
// assertEquals("[A, B, C]", (String) caToString3.invokeExact("ABCD".toCharArray()));
//會(huì)把數(shù)組元素轉(zhuǎn)換成2個(gè)參數(shù)
MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
//會(huì)把數(shù)組元素轉(zhuǎn)換成1個(gè)參數(shù)
MethodHandle caToString1 = caString3.asSpreader(char[].class, 1);
assertEquals("[A, B, C]", (String) caToString1.invokeExact('A','B',"C".toCharArray()));
}
@Test
public void asFixedArityTest() throws Throwable {
MethodHandle asListVar = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
//固定參數(shù)類(lèi)型部服,即不能自動(dòng)轉(zhuǎn)換
MethodHandle asListFix = asListVar.asFixedArity();
assertEquals("[1]", asListVar.invoke(1).toString());
Exception caught = null;
try {
//報(bào)錯(cuò)Cannot cast java.lang.Integer to [Ljava.lang.Object;
asListFix.invoke((Object)1);
} catch (Exception ex) {
caught = ex;
ex.printStackTrace();
}
assert(caught instanceof ClassCastException);
assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
try {
//報(bào)錯(cuò)cannot convert MethodHandle(Object[])List to (String,String)void
asListFix.invoke("two", "too");
} catch (Exception ex) {
caught = ex;
ex.printStackTrace();
}
assert(caught instanceof WrongMethodTypeException);
Object[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
//必須使用Object[]類(lèi)型
assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
//將Object[]類(lèi)型轉(zhuǎn)換成Object時(shí)唆姐,因?yàn)閷?shí)際類(lèi)型是Object[]所以沒(méi)報(bào)錯(cuò)
assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
}
@Test
public void invokeWithArgumentsTest() throws Throwable {
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle methodHandle=lookup.findStatic(Arrays.class,"deepToString", methodType(String.class,Object[].class)).asCollector(Object[].class,3);
//調(diào)用參數(shù)類(lèi)型是Object,Object,Object的方法
String result=(String)methodHandle.invokeWithArguments(1,2,3);
System.out.println(result);
result=(String)methodHandle.invokeWithArguments(Arrays.asList(4,5,6));
System.out.println(result);
//報(bào)錯(cuò)expected (Object,Object,Object)String but found (int,int,int)String
// result=(String)methodHandle.invokeExact(1,2,3);
result=(String)methodHandle.invoke(1,2,3);
System.out.println(result);
}
}
5、MethodHandles
MethodHandles包含一系列的操作MethodHandler的靜態(tài)方法廓八,可以分為以下3類(lèi):
1)創(chuàng)建Lookup實(shí)例
Lookup lookup():該方法返回的Lookup實(shí)例可以訪(fǎng)問(wèn)所有的類(lèi)成員奉芦,包括私有成員,不過(guò)前提是lookup class有訪(fǎng)問(wèn)權(quán)限
Lookup publicLookup():該方法返回的Lookup實(shí)例只能訪(fǎng)問(wèn)公開(kāi)的類(lèi)成員剧蹂,該實(shí)例的lookup class是Object.class
2)數(shù)組操作
MethodHandle arrayElementGetter(Class<?> arrayClass):獲取讀取數(shù)組元素的MethodHandle
MethodHandle arrayElementSetter(Class<?> arrayClass):獲取修改數(shù)組元素的MethodHandle
3)invoker操作
MethodHandle invoker(MethodType type):相當(dāng)于執(zhí)行publicLookup().findVirtual(MethodHandle.class, "invoke", type)声功,獲取的MethodHandle可以將MethodHandle實(shí)例作為接受對(duì)象,調(diào)用MethodHandle實(shí)例綁定的方法宠叼,不過(guò)調(diào)用時(shí)需要通過(guò)invokeWithArguments方法調(diào)用
MethodHandle exactInvoker(MethodType type):相當(dāng)于執(zhí)行publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
MethodHandle spreadInvoker(MethodType type, int leadingArgCount):相當(dāng)于執(zhí)行如下代碼:
4)MethodHandle參數(shù)處理
MethodHandle explicitCastArguments(MethodHandle target, MethodType newType):將MethodHandle的MethodType轉(zhuǎn)換成新的newType先巴,相當(dāng)于MethodHandle#asType()方法,不過(guò)轉(zhuǎn)換限制更少冒冬。
MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder):調(diào)整MethodHandle的入?yún)㈨樞蛏祢牵热缬腥齻€(gè)參數(shù),正常按照0,1,2的順序傳參简烤,如果order位2,0,1則invoke調(diào)用時(shí)第一個(gè)入?yún)?huì)放到方法入?yún)⒌牡谌齻€(gè)參數(shù)剂邮,第二個(gè)參數(shù)放到方法入?yún)⒌牡谝粋€(gè)參數(shù),第三個(gè)參數(shù)放到方法入?yún)⒌牡诙€(gè)參數(shù)乐埠。
MethodHandle insertArguments(MethodHandle target, int pos, Object... values):將從索引pos開(kāi)始的方法入?yún)⒌闹倒潭楹竺娴膙alues抗斤,invoke時(shí)只需要傳其他的非固定值的參數(shù)即可
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes):從入?yún)⑺饕齪os開(kāi)始丟棄valueTypes類(lèi)型的參數(shù)囚企,剩下的參數(shù)用來(lái)調(diào)用target。
MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes); 同上
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters):參數(shù)的預(yù)處理瑞眼,從入?yún)⑺饕齪os開(kāi)始的參數(shù)都執(zhí)行一遍后面的filter龙宏,執(zhí)行結(jié)果作為新的入?yún)⒄{(diào)用target
MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter):同上參數(shù)預(yù)處理,預(yù)處理結(jié)果作為新的入?yún)⒄{(diào)用target伤疙,不過(guò)支持嵌套調(diào)用
MethodHandle foldArguments(MethodHandle target, MethodHandle combiner):同上參數(shù)預(yù)處理银酗,不過(guò)combiner預(yù)處理的結(jié)果是作為新的入?yún)⒉迦氲皆瓉?lái)的入?yún)⒌那懊妫詈笥眠@些參數(shù)執(zhí)行target
MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter):先執(zhí)行target徒像,執(zhí)行結(jié)果作為filter的入?yún)⑹蛱兀瑘?zhí)行filter的結(jié)果作為最后的結(jié)果
5)MethodHandle條件調(diào)用
MethodHandle guardWithTest(MethodHandle test,MethodHandle target,MethodHandle fallback):先執(zhí)行test,如果test返回true锯蛀,則執(zhí)行target灭衷,否則執(zhí)行fallback
MethodHandle catchException(MethodHandle target,Class<? extends Throwable> exType,MethodHandle handler) :執(zhí)行target,如果拋出異常exType旁涤,則執(zhí)行handler
6)特殊的MethodHandle
MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType):返回一個(gè)將指定類(lèi)型exType的異常作為入?yún)⒉伋鲈摦惓5腗ethodHandle翔曲,returnType非空但無(wú)意義
MethodHandle identity(Class<?> type):返回一個(gè)接受指定類(lèi)型的一個(gè)參數(shù)的MethodHandle實(shí)例,會(huì)將入?yún)⒅苯臃祷亍?br>
MethodHandle constant(Class<?> type, Object value):返回一個(gè)沒(méi)有入?yún)⒌腗ethodHandle實(shí)例劈愚,返回固定的值value瞳遍,value的類(lèi)型就是type
測(cè)試用例如下:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.methodType;
import static org.junit.Assert.assertEquals;
public class MethodHandlesTest {
@Test
public void arrayTest() throws Throwable {
int[] a={1,2,3};
//參考源碼對(duì)應(yīng)的數(shù)組讀操作的方法名是getElement
MethodHandle getter= MethodHandles.arrayElementGetter(a.getClass());
//通過(guò)lookup方法返回的Lookup實(shí)例無(wú)法查找上述方法
// MethodHandle getter= MethodHandles.lookup().findStatic(a.getClass(),"getElement", MethodType.methodType(a.getClass().getComponentType(), a.getClass(), int.class));
int a_0=(int)getter.bindTo(a).invoke(0);
int a_1=(int)getter.bindTo(a).invoke(1);
int a_2=(int)getter.bindTo(a).invoke(2);
System.out.println("a="+a_0+","+a_1+","+a_2);
//參考源碼對(duì)應(yīng)的數(shù)組寫(xiě)操作的方法名是setElement
MethodHandle setter= MethodHandles.arrayElementSetter(a.getClass());
setter.invoke(a,0,11);
setter.invoke(a,1,22);
setter.invoke(a,2,33);
System.out.println(Arrays.toString(a));
//Java 反射
a_0=Array.getInt(a,0);
a_1=Array.getInt(a,1);
a_2=Array.getInt(a,2);
System.out.println("a="+a_0+","+a_1+","+a_2);
Array.set(a,0,31);
Array.set(a,1,32);
Array.set(a,2,33);
System.out.println(Arrays.toString(a));
}
@Test
public void invokerTest() throws Throwable {
MethodType methodType= methodType(String.class,String.class,int.class);
MethodHandle target = publicLookup().findStatic(MethodHandlesTest.class, "invokee",
// MethodType.genericMethodType(0, true));
methodType(String.class,String.class,int.class));
//將MethodHandle按照目標(biāo)methodType做適配
target = target.asType(methodType);
MethodHandle inv=MethodHandles.exactInvoker(methodType);
//返回的MethodHandle必須通過(guò)invokeWithArguments調(diào)用,否則各種類(lèi)型轉(zhuǎn)換報(bào)錯(cuò)
String s=(String)inv.invokeWithArguments(target,"test",1);
System.out.println(s);
inv=MethodHandles.invoker(methodType);
s=(String)inv.invokeWithArguments(target,"test",2);
System.out.println(s);
inv=MethodHandles.spreadInvoker(methodType,0);
s=(String)inv.invokeWithArguments(target,new Object[]{"test",3});
System.out.println(s);
inv=MethodHandles.spreadInvoker(methodType,1);
s=(String)inv.invokeWithArguments(target,"test",new Object[]{4});
System.out.println(s);
}
// public static String invokee(Object... args) {
// return Arrays.toString(args);
// }
public static String invokee(String s,int a) {
return "s="+s+",a="+a;
}
@Test
public void ArgumentsTest() throws Throwable {
MethodHandle target = publicLookup().findStatic(MethodHandlesTest.class, "invokee",
methodType(String.class,String.class,int.class));
//報(bào)錯(cuò)cannot convert MethodHandle(Object,Long)String to (String,int)String
// MethodHandle newHandle=MethodHandles.explicitCastArguments(target,MethodType.methodType(String.class,Object.class,Long.class));
//如果執(zhí)行asType方法就可以轉(zhuǎn)換成目標(biāo)type則直接使用asType方法完成轉(zhuǎn)換菌羽,否則嘗試額外的轉(zhuǎn)換
MethodHandle newHandle=MethodHandles.explicitCastArguments(target, methodType(String.class,Object.class,long.class));
String result=(String)newHandle.invoke("ss",2);
System.out.println(result);
}
@Test
public void ArgumentsTest2() throws Throwable {
MethodType intfn1 = methodType(int.class, int.class);
MethodType intfn2 = methodType(int.class, int.class, int.class);
MethodHandle sub = lookup().findStatic(this.getClass(),"sub",intfn2);
assert(sub.type().equals(intfn2));
//通過(guò)reorder改變實(shí)際傳遞參數(shù)的順序
MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
assert((int)rsub.invokeExact(1, 100) == 99);
assert((int)sub1.invokeExact(1, 100) == -99);
MethodHandle add = lookup().findStatic(this.getClass(),"add",intfn2);
assert(add.type().equals(intfn2));
MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
assert(twice.type().equals(intfn1));
assert((int)twice.invokeExact(21) == 42);
}
public static int sub(int x,int y){
return x-y;
}
public static int add(int x,int y){
return x+y;
}
public static int add(int x,int y,int z){
return x+y+z;
}
@Test
public void ArgumentsTest3() throws Throwable {
MethodHandles.Lookup lookup= lookup();
MethodType intfn4 = methodType(int.class, int.class, int.class,int.class);
MethodType intfn3 = methodType(int.class, int.class, int.class);
MethodType intfn2 = methodType(int.class, int.class);
MethodHandle add=lookup.findStatic(this.getClass(),"add",intfn4);
int result=(int)add.invoke(1,2,3);
System.out.println(result);
//將4作為參數(shù)索引為2的參數(shù)的綁定值掠械,即該參數(shù)固定為4,前面索引1,2的參數(shù)由invoke方法傳入
//使用固定參數(shù)后注祖,該參數(shù)對(duì)應(yīng)的參數(shù)類(lèi)型從原來(lái)的MethodType中去除
MethodHandle addNew=MethodHandles.insertArguments(add,2,4);
result=(int)addNew.invoke(1,2);
System.out.println(result);
assertEquals(addNew.type(),intfn3);
addNew=MethodHandles.insertArguments(add,1,5,6);
result=(int)addNew.invoke(1);
System.out.println(result);
assertEquals(addNew.type(),intfn2);
}
@Test
public void ArgumentsTest4() throws Throwable {
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);
System.out.println(bigType);
List<Class<?>> classList=bigType.parameterList().subList(0,2);
System.out.println(classList);
//生成的MethodHandle會(huì)丟棄掉從參數(shù)索引0開(kāi)始的classList的參數(shù)猾蒂,使用剩余的參數(shù)調(diào)用原來(lái)cat綁定的方法
// MethodHandle d0 = dropArguments(cat, 0, classList);
MethodHandle d0 = dropArguments(cat, 0, int.class, String.class);
//為了跟丟棄的參數(shù)適配,生成的MethodHandle的MethodType相當(dāng)于在原來(lái)的MethodType的基礎(chǔ)上增加classList對(duì)應(yīng)的參數(shù)類(lèi)型
assertEquals(bigType, d0.type());
assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
d0 = dropArguments(cat, 1, int.class, String.class);
assertEquals("xz", (String) d0.invokeExact( "x",123, "y", "z"));
}
@Test
public void ArgumentsTest5() throws Throwable {
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle upcase = lookup().findVirtual(String.class,
"toUpperCase", methodType(String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
//filterArguments添加若干個(gè)參數(shù)的預(yù)處理MethodHandle氓轰,cat執(zhí)行前會(huì)使用upcase從指定的參數(shù)開(kāi)始預(yù)處理
MethodHandle f0 = filterArguments(cat, 0, upcase);
assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
MethodHandle f1 = filterArguments(cat, 1, upcase);
assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
}
@Test
public void ArgumentsTest6() throws Throwable {
MethodHandle deepToString = publicLookup()
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
MethodHandle ts1 = deepToString.asCollector(String[].class, 1);
assertEquals("[strange]", (String) ts1.invokeExact("strange"));
MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));
MethodHandle ts3 = deepToString.asCollector(String[].class, 3);
//從參數(shù)索引1的位置開(kāi)始的參數(shù)執(zhí)行ts2婚夫,將執(zhí)行結(jié)果作為參數(shù)執(zhí)行ts3
MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);
assertEquals("[top, [up, down], strange]",
(String) ts3_ts2.invokeExact("top", "up", "down", "strange"));
MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);
assertEquals("[top, [up, down], [strange]]",
(String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));
//從參數(shù)索引1的位置開(kāi)始的參數(shù)執(zhí)行ts3,將執(zhí)行結(jié)果作為參數(shù)執(zhí)行ts3_ts2
MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);
assertEquals("[top, [[up, down, strange], charm], bottom]",
(String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
}
@Test
public void ArgumentsTest7() throws Throwable {
MethodHandle upcase = lookup().findVirtual(String.class,
"toUpperCase", methodType(String.class));
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
//upcase也是預(yù)處理署鸡,不同的是預(yù)處理的結(jié)果會(huì)做一個(gè)新的參數(shù)插入到原來(lái)的參數(shù)列表的前面案糙,然后執(zhí)行cat
MethodHandle catTrace = foldArguments(cat, upcase);
assertEquals("BOOboo", (String) catTrace.invokeExact("boo"));
}
@Test
public void ArgumentsTest8() throws Throwable {
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle length = lookup().findVirtual(String.class,
"length", methodType(int.class));
System.out.println((String) cat.invokeExact("x", "y")); // xy
//filterReturnValue是后處理,先執(zhí)行cat靴庆,執(zhí)行的結(jié)果作為參數(shù)執(zhí)行l(wèi)ength时捌,返回最終的結(jié)果
MethodHandle f0 = filterReturnValue(cat, length);
System.out.println((int) f0.invokeExact("x", "y")); // 2
}
@Test
public void specialMethodHandleTest() throws Throwable {
//返回的MethodHandle接收特定類(lèi)型的異常的實(shí)例,然后拋出異常
MethodHandle methodHandle=MethodHandles.throwException(void.class,UnsupportedOperationException.class);
methodHandle.invoke(new UnsupportedOperationException());
}
@Test
public void specialMethodHandleTest2() throws Throwable {
//返回的MethodHandle接收指定類(lèi)型的唯一一個(gè)參數(shù)炉抒,并將其返回
MethodHandle methodHandle=MethodHandles.identity(String.class);
System.out.println((String)methodHandle.invoke("s"));
}
@Test
public void specialMethodHandleTest3() throws Throwable {
//返回的MethodHandle實(shí)例永遠(yuǎn)返回一個(gè)指定類(lèi)型的固定的值奢讨,沒(méi)有任何入?yún)? MethodHandle methodHandle=MethodHandles.constant(String.class,"test");
System.out.println((String)methodHandle.invoke());
System.out.println((String)methodHandle.invoke());
}
}
三、與Java Reflect的區(qū)別
可以用Java Reflect實(shí)現(xiàn)第一個(gè)測(cè)試用例的效果么焰薄?答案是可以拿诸,如下:
import java.lang.reflect.Method;
class TestA{
public void println(String s){
System.out.println("TestA println:"+s);
}
}
class TestB{
public void println(String s){
System.out.println("TestB println:"+s);
}
}
public class MethodHandlerTest {
public static void reflectTest(Object obj,String s) throws Throwable{
Method method=obj.getClass().getMethod("println",String.class);
method.invoke(obj,s);
}
public static void main(String[] args) throws Throwable{
String test="Hello World";
reflectTest(new TestA(),test);
reflectTest(new TestB(),test);
reflectTest(System.out,test);
}
}
上述示例和之前使用MethodHandle操作字段扒袖,普通方法,構(gòu)造方法及數(shù)組的示例中亩码,我們用Java反射的API執(zhí)行了相同操作季率,反射的代碼明顯比MethodHandle更簡(jiǎn)潔明了,那MethodHandle的價(jià)值在哪描沟?
MethodHandle最大的價(jià)值在于其更輕量飒泻,只包含方法調(diào)用必要的信息,是對(duì)方法調(diào)用句柄而非方法本身的抽象吏廉;反射中的Method類(lèi)是Java方法在Java語(yǔ)言層面的一個(gè)完整抽象泞遗,包含方法的注解,參數(shù)類(lèi)型席覆,返回類(lèi)型史辙,修飾符等跟方法調(diào)用本身不直接相關(guān)的東西。
MethodHandle在執(zhí)行時(shí)不會(huì)檢查是否具有調(diào)用權(quán)限佩伤,只在MethodHandle創(chuàng)建時(shí)檢查髓霞;而Method類(lèi)執(zhí)行的方法調(diào)用或者屬性操作等是每次執(zhí)行都會(huì)檢查是否具有調(diào)用權(quán)限,因此MethodHandle可以省掉一次調(diào)用權(quán)限檢查的損耗
MethodHandle的底層實(shí)現(xiàn)是模擬字節(jié)碼的行為畦戒,而Java反射只是Java層面的層層方法調(diào)用,調(diào)用鏈更長(zhǎng)结序,理論上MethodHandle的性能更好障斋。