欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

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

什么是方法句柄?

如API文件中所述,關于其定義:

方法句柄是對基礎方法、構造函數(shù)、字段或類似低級操作的類型化、直接可執(zhí)行的引用,具有參數(shù)或返回值的可選轉換。

更簡單地說,方法句柄是一種用于查找、調整和調用方法的低級機制。

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

要創(chuàng)建和使用MethodHandle,需要4個步驟:

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

方法句柄與反射

引入方法句柄是為了與現(xiàn)有的java.lang.reflect API一起工作,因為它們具有不同的用途和不同的特性。

從性能角度來看,MethodHandles API可能比Reflection API快得多,因為訪問檢查是在創(chuàng)建時而不是在執(zhí)行時進行的。如果存在安全管理器,則這種差異會被放大,因為成員和類查找要接受額外的檢查。

然而,考慮到性能并不是任務的唯一適用性度量,我們還必須考慮到,由于缺乏成員類枚舉、可訪問性標志檢查等機制,MethodHandles API更難使用。

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

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

創(chuàng)建Lookup

當我們想要創(chuàng)建方法句柄時,要做的第一件事是檢索查找Lookup,即負責為查找類可見的方法、構造函數(shù)和字段創(chuàng)建方法句柄的工廠對象。

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

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

MethodHandles.Lookup publicLookup = MethodHandles.publicLookup();

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

MethodHandles.Lookup lookup = MethodHandles.lookup();

創(chuàng)建MethodType

為了能夠創(chuàng)建MethodHandle,查找對象需要其類型的定義,這是通過MethodType類實現(xiàn)的。

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

MethodType的結構很簡單,它由一個返回類型和適當數(shù)量的參數(shù)類型組成,這些參數(shù)類型必須在方法句柄及其所有調用方之間正確匹配。

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

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

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

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

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

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

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

找到方法句柄

一旦我們定義了方法類型,為了創(chuàng)建MethodHandle,我們必須通過lookuppublicLookup對象找到它,同時提供原始類和方法名稱。

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

方法的MethodHandle

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

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

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

當我們想要訪問靜態(tài)方法時,我們可以使用findStatic()方法:

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

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

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

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

可以使用findConstructor()方法訪問構造函數(shù)。

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

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

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

字段的方法句柄

使用方法句柄也可以訪問字段。

讓我們開始定義Book類:

public class Book {
    
    String id;
    String title;

    // constructor

}

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

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

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

私有方法的方法句柄

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

讓我們開始向Book類添加一個私有方法:

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

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

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

MethodHandle formatBookMH = lookup.unreflect(formatBookMethod);

調用方法句柄

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

讓我們從invoke選項開始。

當使用invoke()方法時,我們強制要固定的參數(shù)數(shù)量,但我們允許執(zhí)行參數(shù)和返回類型的強制轉換和裝箱/取拆箱。

讓我們看看如何使用帶框參數(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í)行之前會對Character參數(shù)執(zhí)行開箱操作。

使用參數(shù)調用

使用invokeWithArguments方法調用方法句柄是三個選項中限制最小的一個。

事實上,除了參數(shù)和返回類型的強制轉換和裝箱/取消裝箱外,它還允許變量arity調用。

在實踐中,這允許我們從一個int值數(shù)組開始創(chuàng)建一個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));

調用Exact

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

事實上,它沒有為所提供的類提供任何類型轉換,并且需要固定數(shù)量的參數(shù)。

讓我們看看如何使用方法句柄對兩個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方法傳遞一個不是int的數(shù)字,那么調用將導致WrongMethodTypeException。

使用數(shù)組

MethodHandles不僅用于字段或對象,還用于數(shù)組。事實上,使用asSpreader()API,可以生成一個數(shù)組擴展方法句柄。

在這種情況下,方法句柄接受一個數(shù)組參數(shù),將其元素擴展為位置參數(shù),并可以選擇數(shù)組的長度。

讓我們看看如何擴展方法句柄來檢查數(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" }));

增強方法句柄

一旦我們定義了一個方法句柄,就可以通過將方法句柄綁定到一個參數(shù)來增強它,而無需實際調用它。

例如,在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增強功能

在Java9中,對MethodHandles API進行了一些增強,目的是使其更易于使用。

這些增強影響了3個主要主題:

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

這些變化帶來了一些額外的好處:

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

結論

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

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

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

相關文章

最新評論