MethodHandle詳解

從《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ò),如下圖:

image.png

如果是方法調(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é)果如下:

image.png

聲明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ò):

image.png

上述代碼如果改成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é)果如下:


image.png

變量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é)果如下:


image.png

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ò)肩狂,如下圖:

image.png

屬性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é)果如下:


image.png

通過(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)方法

image.png

其中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)型的方法

image.png

因?yàn)镸ethodType是不可變的绽淘,所以上述方法實(shí)際是在當(dāng)前MethodType的基礎(chǔ)上做適當(dāng)修改然后構(gòu)造了一個(gè)新的MethodType實(shí)例。

3)讀取返回參數(shù)類(lèi)型和方法參數(shù)類(lèi)型的方法

image.png

其中 hasPrimitives方法判斷MethodType的參數(shù)類(lèi)型和返回類(lèi)型中是否包含基本類(lèi)型闹伪,hasWrappers判斷是否包含包裝器類(lèi)型沪铭,如Interger壮池。

4)對(duì)當(dāng)前的MethodType進(jìn)行轉(zhuǎn)換的便利方法


image.png

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)換的方法:

image.png

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ō)明吱肌。

image.png

表中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í)行如下代碼:

image.png

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的性能更好障斋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市徐鹤,隨后出現(xiàn)的幾起案子垃环,更是在濱河造成了極大的恐慌,老刑警劉巖返敬,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遂庄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡劲赠,警方通過(guò)查閱死者的電腦和手機(jī)涛目,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)凛澎,“玉大人霹肝,你說(shuō)我怎么就攤上這事∷芗澹” “怎么了沫换?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)最铁。 經(jīng)常有香客問(wèn)我讯赏,道長(zhǎng)垮兑,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任漱挎,我火速辦了婚禮系枪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘识樱。我一直安慰自己嗤无,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布怜庸。 她就那樣靜靜地躺著当犯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪割疾。 梳的紋絲不亂的頭發(fā)上嚎卫,一...
    開(kāi)封第一講書(shū)人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音宏榕,去河邊找鬼拓诸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛麻昼,可吹牛的內(nèi)容都是我干的奠支。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼抚芦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼倍谜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起叉抡,我...
    開(kāi)封第一講書(shū)人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤尔崔,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后褥民,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體季春,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年消返,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了载弄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侦副,死狀恐怖侦锯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秦驯,我是刑警寧澤尺碰,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響亲桥,放射性物質(zhì)發(fā)生泄漏洛心。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一题篷、第九天 我趴在偏房一處隱蔽的房頂上張望词身。 院中可真熱鬧,春花似錦番枚、人聲如沸法严。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)深啤。三九已至,卻和暖如春路星,著一層夾襖步出監(jiān)牢的瞬間溯街,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工洋丐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呈昔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓友绝,卻偏偏與公主長(zhǎng)得像堤尾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迁客,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容