亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java?MethodHandles介紹與反射對(duì)比區(qū)別詳解

 更新時(shí)間:2023年11月28日 09:32:46   作者:架構(gòu)狂魔哥  
這篇文章主要為大家介紹了Java?MethodHandles介紹與反射對(duì)比區(qū)別詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在本文中,我們將探討一個(gè)重要的API,它是在Java7中引入的,并在以后的jdk版本中得到了增強(qiáng),即java.lang.invoke.MethodHandles。

特別是,我們將學(xué)習(xí)什么是方法句柄(method handles),如何創(chuàng)建它們以及如何使用它們。

什么是方法句柄?

如API文件中所述,關(guān)于其定義:

方法句柄是對(duì)基礎(chǔ)方法、構(gòu)造函數(shù)、字段或類(lèi)似低級(jí)操作的類(lèi)型化、直接可執(zhí)行的引用,具有參數(shù)或返回值的可選轉(zhuǎn)換。

更簡(jiǎn)單地說(shuō),方法句柄是一種用于查找、調(diào)整和調(diào)用方法的低級(jí)機(jī)制。

方法句柄是不可變的,并且沒(méi)有可見(jiàn)的狀態(tài)。

要?jiǎng)?chuàng)建和使用MethodHandle,需要4個(gè)步驟:

  • 創(chuàng)建lookup
  • 創(chuàng)建method type
  • 查找方法句柄
  • 調(diào)用方法句柄

方法句柄與反射

引入方法句柄是為了與現(xiàn)有的java.lang.reflect API一起工作,因?yàn)樗鼈兙哂胁煌挠猛竞筒煌奶匦浴?/p>

從性能角度來(lái)看,MethodHandles API可能比Reflection API快得多,因?yàn)樵L問(wèn)檢查是在創(chuàng)建時(shí)而不是在執(zhí)行時(shí)進(jìn)行的。如果存在安全管理器,則這種差異會(huì)被放大,因?yàn)槌蓡T和類(lèi)查找要接受額外的檢查。

然而,考慮到性能并不是任務(wù)的唯一適用性度量,我們還必須考慮到,由于缺乏成員類(lèi)枚舉、可訪問(wèn)性標(biāo)志檢查等機(jī)制,MethodHandles API更難使用。

即便如此,MethodHandles API提供了柯里化方法、更改參數(shù)類(lèi)型和更改其順序的可能性。

有了MethodHandles API的清晰定義和目標(biāo),我們現(xiàn)在可以從lookup開(kāi)始使用它們。

創(chuàng)建Lookup

當(dāng)我們想要?jiǎng)?chuàng)建方法句柄時(shí),要做的第一件事是檢索查找Lookup,即負(fù)責(zé)為查找類(lèi)可見(jiàn)的方法、構(gòu)造函數(shù)和字段創(chuàng)建方法句柄的工廠對(duì)象。

通過(guò)MethodHandles API,可以創(chuàng)建具有不同訪問(wèn)模式的查找對(duì)象。

讓我們創(chuàng)建一個(gè)提供對(duì)公共方法訪問(wèn)的查找:

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();

然而,如果我們也想訪問(wèn)私有和受保護(hù)的方法,我們可以使用lookup()方法:

MethodHandles.Lookup lookup = MethodHandles.lookup();

創(chuàng)建MethodType

為了能夠創(chuàng)建MethodHandle,查找對(duì)象需要其類(lèi)型的定義,這是通過(guò)MethodType類(lèi)實(shí)現(xiàn)的。

特別是,MethodType表示方法句柄接受和返回的參數(shù)和返回類(lèi)型,或方法句柄調(diào)用程序傳遞和期望的參數(shù)和返回類(lèi)型。

MethodType的結(jié)構(gòu)很簡(jiǎn)單,它由一個(gè)返回類(lèi)型和適當(dāng)數(shù)量的參數(shù)類(lèi)型組成,這些參數(shù)類(lèi)型必須在方法句柄及其所有調(diào)用方之間正確匹配。

與MethodHandle相同,即使是MethodType的實(shí)例也是不可變的。

讓我們看看如何定義一個(gè)MethodType,該MethodType將java.util.List類(lèi)指定為返回類(lèi)型,將Object數(shù)組指定為輸入類(lèi)型:

MethodType mt = MethodType.methodType(List.class, Object[].class);

如果該方法返回基本類(lèi)型或void作為其返回類(lèi)型,我們將使用表示這些類(lèi)型的類(lèi)(void.class、int.class…)。

讓我們定義一個(gè)返回int值并接受Object的MethodType:

MethodType mt = MethodType.methodType(int.class, Object.class);

我們現(xiàn)在可以繼續(xù)創(chuàng)建MethodHandle。

找到方法句柄

一旦我們定義了方法類(lèi)型,為了創(chuàng)建MethodHandle,我們必須通過(guò)lookuppublicLookup對(duì)象找到它,同時(shí)提供原始類(lèi)和方法名稱(chēng)。

特別是,查找工廠提供了一組方法,使我們能夠在考慮方法范圍的情況下以適當(dāng)?shù)姆绞秸业椒椒ň浔?。從最?jiǎn)單的場(chǎng)景開(kāi)始,讓我們探究主要的場(chǎng)景。

方法的MethodHandle

使用findVirtual()方法可以為對(duì)象方法創(chuàng)建一個(gè)MethodHandle。讓我們根據(jù)String類(lèi)的concat()方法創(chuàng)建一個(gè):

MethodType mt = MethodType.methodType(String.class, String.class);
MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt);

靜態(tài)方法的方法句柄

當(dāng)我們想要訪問(wèn)靜態(tài)方法時(shí),我們可以使用findStatic()方法:

MethodType mt = MethodType.methodType(List.class, Object[].class);

MethodHandle asListMH = publicLookup.findStatic(Arrays.class, "asList", mt);

在本例中,我們創(chuàng)建了一個(gè)方法句柄,用于將對(duì)象數(shù)組轉(zhuǎn)換為對(duì)象列表。

構(gòu)造函數(shù)的方法句柄

可以使用findConstructor()方法訪問(wèn)構(gòu)造函數(shù)。

讓我們創(chuàng)建一個(gè)方法句柄,它充當(dāng)Integer類(lèi)的構(gòu)造函數(shù),接受String屬性:

MethodType mt = MethodType.methodType(void.class, String.class);

MethodHandle newIntegerMH = publicLookup.findConstructor(Integer.class, mt);

字段的方法句柄

使用方法句柄也可以訪問(wèn)字段。

讓我們開(kāi)始定義Book類(lèi):

public class Book {
    
    String id;
    String title;

    // constructor

}

先決條件是方法句柄和聲明的屬性之間具有直接訪問(wèn)可見(jiàn)性,我們可以創(chuàng)建一個(gè)充當(dāng)getter的方法句柄:

MethodHandle getTitleMH = lookup.findGetter(Book.class, "title", String.class);

有關(guān)處理變量/字段的更多信息,請(qǐng)參閱Java 9 Variable Handles:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/...

私有方法的方法句柄

java.lang.reflect API的幫助下,可以為私有方法創(chuàng)建方法句柄。

讓我們開(kāi)始向Book類(lèi)添加一個(gè)私有方法:

private String formatBook() {
    return id + " > " + title;
}

現(xiàn)在,我們可以創(chuàng)建一個(gè)與formatBook()方法完全相同的方法句柄:

Method formatBookMethod = Book.class.getDeclaredMethod("formatBook");
formatBookMethod.setAccessible(true);

MethodHandle formatBookMH = lookup.unreflect(formatBookMethod);

調(diào)用方法句柄

一旦我們創(chuàng)建了方法句柄,下一步就是使用它們。特別是,MethodHandle類(lèi)提供了3種不同的方法來(lái)執(zhí)行方法句柄:invoke()、invokeWithAruments()invokeExact()

讓我們從invoke選項(xiàng)開(kāi)始。

當(dāng)使用invoke()方法時(shí),我們強(qiáng)制要固定的參數(shù)數(shù)量,但我們?cè)试S執(zhí)行參數(shù)和返回類(lèi)型的強(qiáng)制轉(zhuǎn)換和裝箱/取拆箱。

讓我們看看如何使用帶框參數(shù)的invoke()

MethodType mt = MethodType.methodType(String.class, char.class, char.class);
MethodHandle replaceMH = publicLookup.findVirtual(String.class, "replace", mt);

String output = (String) replaceMH.invoke("jovo", Character.valueOf('o'), 'a');

assertEquals("java", output);

在這種情況下,replaceMH需要char參數(shù),但invoke()在執(zhí)行之前會(huì)對(duì)Character參數(shù)執(zhí)行開(kāi)箱操作。

使用參數(shù)調(diào)用

使用invokeWithArguments方法調(diào)用方法句柄是三個(gè)選項(xiàng)中限制最小的一個(gè)。

事實(shí)上,除了參數(shù)和返回類(lèi)型的強(qiáng)制轉(zhuǎn)換和裝箱/取消裝箱外,它還允許變量arity調(diào)用。

在實(shí)踐中,這允許我們從一個(gè)int值數(shù)組開(kāi)始創(chuàng)建一個(gè)Integer列表:

MethodType mt = MethodType.methodType(List.class, Object[].class);
MethodHandle asList = publicLookup.findStatic(Arrays.class, "asList", mt);

List<Integer> list = (List<Integer>) asList.invokeWithArguments(1,2);

assertThat(Arrays.asList(1,2), is(list));

調(diào)用Exact

如果我們想在執(zhí)行方法句柄的方式上更加嚴(yán)格(參數(shù)的數(shù)量及其類(lèi)型),我們必須使用invokeExact()方法。

事實(shí)上,它沒(méi)有為所提供的類(lèi)提供任何類(lèi)型轉(zhuǎn)換,并且需要固定數(shù)量的參數(shù)。

讓我們看看如何使用方法句柄對(duì)兩個(gè)int值求和:

MethodType mt = MethodType.methodType(int.class, int.class, int.class);
MethodHandle sumMH = lookup.findStatic(Integer.class, "sum", mt);

int sum = (int) sumMH.invokeExact(1, 11);

assertEquals(12, sum);

如果在這種情況下,我們決定向invokeExact方法傳遞一個(gè)不是int的數(shù)字,那么調(diào)用將導(dǎo)致WrongMethodTypeException。

使用數(shù)組

MethodHandles不僅用于字段或?qū)ο螅€用于數(shù)組。事實(shí)上,使用asSpreader()API,可以生成一個(gè)數(shù)組擴(kuò)展方法句柄。

在這種情況下,方法句柄接受一個(gè)數(shù)組參數(shù),將其元素?cái)U(kuò)展為位置參數(shù),并可以選擇數(shù)組的長(zhǎng)度。

讓我們看看如何擴(kuò)展方法句柄來(lái)檢查數(shù)組中的元素是否相等:

MethodType mt = MethodType.methodType(boolean.class, Object.class);
MethodHandle equals = publicLookup.findVirtual(String.class, "equals", mt);

MethodHandle methodHandle = equals.asSpreader(Object[].class, 2);

assertTrue((boolean) methodHandle.invoke(new Object[] { "java", "java" }));

增強(qiáng)方法句柄

一旦我們定義了一個(gè)方法句柄,就可以通過(guò)將方法句柄綁定到一個(gè)參數(shù)來(lái)增強(qiáng)它,而無(wú)需實(shí)際調(diào)用它。

例如,在Java9中,這種行為用于優(yōu)化字符串連接。

讓我們看看如何執(zhí)行串聯(lián),將后綴綁定到我們的concatMH

MethodType mt = MethodType.methodType(String.class, String.class);
MethodHandle concatMH = publicLookup.findVirtual(String.class, "concat", mt);

MethodHandle bindedConcatMH = concatMH.bindTo("Hello ");

assertEquals("Hello World!", bindedConcatMH.invoke("World!"));

Java 9增強(qiáng)功能

在Java9中,對(duì)MethodHandles API進(jìn)行了一些增強(qiáng),目的是使其更易于使用。

這些增強(qiáng)影響了3個(gè)主要主題:

  • 查找函數(shù)–允許從不同上下文中查找類(lèi),并支持接口中的非抽象方法
  • 參數(shù)處理——改進(jìn)參數(shù)折疊、參數(shù)收集和參數(shù)傳播功能
  • 附加組合–添加循環(huán)(loop、whileLoop、doWhileLoop…),并通過(guò)tryFinally提供更好的異常處理支持

這些變化帶來(lái)了一些額外的好處:

  • 增加JVM編譯器優(yōu)化
  • 實(shí)例化減少
  • 在使用MethodHandles API時(shí)啟用了精度

結(jié)論

在本文中,我們介紹了MethodHandles API、它們是什么以及如何使用它們。

我們還討論了它與反射API的關(guān)系,由于方法句柄允許低級(jí)別操作,因此最好避免使用它們,除非它們完全適合工作范圍。

以上就是Java MethodHandles介紹與反射對(duì)比區(qū)別詳解的詳細(xì)內(nèi)容,更多關(guān)于Java MethodHandles對(duì)比反射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論