日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java实现泛型检索_高级Java泛型:检索泛型类型参数

發(fā)布時間:2023/12/3 java 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java实现泛型检索_高级Java泛型:检索泛型类型参数 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

java實現(xiàn)泛型檢索

在JDK5中引入Java泛型之后, Java泛型Swift成為許多Java程序的組成部分。 但是,乍一看似乎很簡單的Java泛型,程序員很快就會迷失此功能。

大多數(shù)Java程序員都知道Java編譯器的類型擦除 。 一般而言,類型擦除意味著有關(guān)Java類的所有通用類型信息在其源代碼的編譯過程中都會丟失。 這是對Java向后兼容性的致敬:Java類的所有通用變體在正在運行的Java應用程序中共享一個表示。 如果ArrayList <String>的實例必須記住其泛型類型為String類型,則它必須將該信息存儲在其功能描述中的某個位置,以指示例如List.get實際上返回String類型。 (通過功能描述,我指的是在類的所有實例之間共享的屬性。這包括例如方法或字段定義。與功能描述相反,每個實例各自的實例狀態(tài)存儲在其對象表示中。 )因此ArrayList <String>實例的功能描述由其類ArrayList.class表示。 但是,由于ArrayList.class實例與其他實例(也可以是ArrayList <Integer>類型)共享,因此這將需要具有ArrayList.class的兩個不同版本。 但是,這種類表示形式的修改對于較舊的JRE來說是無法理解的,因此會破壞Java應用程序的向后兼容性。 因此,以下比較將始終成功:

assert new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass();

由于這種比較是在運行時進行的,而該運行時已經(jīng)擦除了類的通用類型,因此該比較將ArrayList.class == ArrayList.class轉(zhuǎn)化為瑣碎的事情。 更具體地說,正在運行的應用程序?qū)⒋_定ArrayList.class等于其自身,并返回true,而不管String.class!= Integer.class。 這是Java與其他編程語言(例如C ++)的主要區(qū)別,也是人們普遍抱怨Java的原因。 (從學術(shù)上講,C ++實際上并不知道泛型類型。相反, C ++提供了與泛型相似的模板 。)

到目前為止,這對于許多開發(fā)人員而言并不是什么新鮮事物。 但是,與普遍看法相反,有時甚至在運行時也可能檢索通用類型信息。 在解釋之前,讓我們看一個例子。 為此,我們定義以下兩個類:

class MyGenericClass<T> { } class MyStringSubClass extends MyGenericClass<String> { }

MyGenericClass具有通用類型T的單個參數(shù)。MyStringSubClass擴展了該通用類,并將T = String分配為其類型參數(shù)。 結(jié)果,Java編譯器能夠在其子類 MyStringSubClass的字節(jié)碼中存儲有關(guān)超類MyGenericClass的通用參數(shù)類型String的信息。 可以在不破壞向后兼容性的情況下實現(xiàn)此修改,因為此信息僅存儲在已編譯類的字節(jié)碼的區(qū)域中,而舊JRE版本會忽略該信息。 同時,由于為MyStringSubClass的所有實例設置了T = String,因此MyStringSubClass的所有實例仍可以共享單個類表示形式。

但是,如何獲取存儲在字節(jié)碼中的信息呢? Java API提供了Class.getGenericSuperclass方法,該方法可用于接收Type類型的實例。 如果直接超類實際上是泛型的,則返回的實例的類型另外為ParameterizedType,并且可以強制轉(zhuǎn)換為該實例。 (類型不過是標記接口 。實際實例將是內(nèi)部ParameterizedTypeImpl類的實例,但是您應始終將其強制轉(zhuǎn)換為該接口。)由于強制轉(zhuǎn)換為ParameterizedType接口,您現(xiàn)在可以調(diào)用方法ParameterizedType.getActualTypeArguments檢索再次為Type類型的數(shù)組。 泛型超類的任何泛型類型參數(shù)將包含在此數(shù)組中,與類型定義中的索引相同。 任何表示非泛型類的Type實例都只是Java類類的實現(xiàn)。 (假定您不處理返回類型為GenericArrayType的數(shù)組。為簡單起見,本文將跳過此方案。)

現(xiàn)在,我們可以利用這些知識來編寫實用函數(shù):

public static Class<?> findSuperClassParameterType(Object instance, Class<?> classOfInterest, int parameterIndex) {Class<?> subClass = instance.getClass();while (subClass != subClass.getSuperclass()) {// instance.getClass() is no subclass of classOfInterest or instance is a direct instance of classOfInterestsubClass = subClass.getSuperclass();if (subClass == null) throw new IllegalArgumentException();}ParameterizedType parameterizedType = (ParameterizedType) subClass.getGenericSuperclass();return (Class<?>) parameterizedType.getActualTypeArguments()[parameterIndex]; }

該函數(shù)將瀏覽實例的類層次結(jié)構(gòu),直到將classOfInterest識別為層次結(jié)構(gòu)中的下一個直接子類。 在這種情況下,將使用Class.getGenericSuperclass方法檢索此超類。 如上所述,此方法以包裝表示形式(ParamererizedType)返回類的超類,該表示形式包含在子類中找到的泛型類型。 這使我們能夠成功運行以下應用程序:

Class<?> genericType = findSuperClassParameterType(new MyStringSubClass(), MyGenericClass.class, 0); assert genericType == String.class;

但是請注意

findSuperClassParamerterType(new MyGenericClass<String>(), MyGenericClass.class, 0)

在此實現(xiàn)中將引發(fā)異常。 如前所述:通用信息只能在子類的幫助下進行檢索。 但是,MyGenericClass <String>不是MyGenericClass.class的子類,而是具有泛型參數(shù)的直接實例。 但是,如果沒有顯式的子類,就沒有<something> .class表示形式來存儲String參數(shù)。 因此,這次,在編譯過程中無法刪除通用類型。 因此,如果打算對一個類執(zhí)行此類查詢,則最好將MyGenericClass定義為抽象。

但是,由于到目前為止存在許多陷阱,我們?nèi)晕唇鉀Q問題。 為了說明原因,請考慮以下類層次結(jié)構(gòu):

class MyGenericClass<T> { } class MyGenericSubClass<U> extends MyGenericClass<U> class MyStringSubSubClass extends MyGenericSubClass<String> { }

如果我們現(xiàn)在打電話

findSuperClassParameterType(new MyStringSubClass(), MyGenericClass.class, 0);

將會引發(fā)異常。 但是為什么會這樣呢? 到目前為止,我們假定MyGenericClass的類型參數(shù)T存儲在直接子類中。 在我們的第一個示例中,這是MyStringSubClass,它映射了通用參數(shù)T = String。 相反,現(xiàn)在MyStringSubSubClass存儲引用U = String,而MyGenericSubClass只知道U =T。但是,U不是實際類,而是Java類型TypeVariable的類型變量。 如果要解析此層次結(jié)構(gòu),則必須解析所有這些依賴項。 這可以通過調(diào)整示例代碼來實現(xiàn):

public static Class<?> findSubClassParameterType(Object instance, Class<?> classOfInterest, int parameterIndex) {Map<Type, Type> typeMap = new HashMap<Type, Type>();Class<?> instanceClass = instance.getClass();while (classOfInterest != instanceClass.getSuperclass()) {extractTypeArguments(typeMap, instanceClass);instanceClass = instanceClass.getSuperclass();if (instanceClass == null) throw new IllegalArgumentException();}ParameterizedType parameterizedType = (ParameterizedType) instanceClass.getGenericSuperclass();Type actualType = parameterizedType.getActualTypeArguments()[parameterIndex];if (typeMap.containsKey(actualType)) {actualType = typeMap.get(actualType);}if (actualType instanceof Class) {return (Class<?>) actualType;} else {throw new IllegalArgumentException();}private static void extractTypeArguments(Map<Type, Type> typeMap, Class<?> clazz) {Type genericSuperclass = clazz.getGenericSuperclass();if (!(genericSuperclass instanceof ParameterizedType)) {return;}ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;Type[] typeParameter = ((Class<?>) parameterizedType.getRawType()).getTypeParameters();Type[] actualTypeArgument = parameterizedType.getActualTypeArguments();for (int i = 0; i < typeParameter.length; i++) {if(typeMap.containsKey(actualTypeArgument[i])) {actualTypeArgument[i] = typeMap.get(actualTypeArgument[i]);}typeMap.put(typeParameter[i], actualTypeArgument[i]);} }

上面的代碼將通過在映射中跟蹤任何鏈接的泛型類型定義來解析它們。 請注意,由于MyClass <A,B>擴展了MyOtherClass <B,A>定義了完全合法的子類型,因此僅按特定索引檢查所有類型定義是不夠的。

但是,我們?nèi)匀粵]有完成。 再次,我們將首先看一個示例:

class MyGenericOuterClass<U> {public class MyGenericInnerClass<U> { } } class MyStringOuterSubClass extends MyGenericOuterClass<String> { }MyStringOuterSubClass.MyGenericInnerClass inner = new MyStringOuterSubClass().new MyGenericInnerClass();

這次通過調(diào)用內(nèi)部類的反思

findSuperClassParameterType(inner, MyGenericInnerClass.class, 0);

將失敗。 乍一看,這似乎是必然的結(jié)果。 我們正在同一類的實例上的MyGenericInnerClass中尋找通用參數(shù)類型。 如上所述,這通常是不可能的,因為無法將通用類型信息存儲在MyGenericInnerClass.class中。 但是,在這里,我們檢查了泛型類的子類型的(非靜態(tài))內(nèi)部類的實例。 MyStringOuterSubClass知道U =字符串。 在考慮MyGenericInnterClass的參數(shù)類型時,我們必須考慮到這一點。

現(xiàn)在,這里的事情變得非常棘手。 為了在外部類中找到泛型聲明,我們必須首先掌握該外部類。 這可以通過反射和Java編譯器增加了一個這樣的事實來實現(xiàn)的合成 (這意味著沒有源代碼表示)字段此$ 0到任何內(nèi)部類。 可以通過調(diào)用Class.getDeclaredField(“ this $ 0”)來檢索此字段。 通過獲取包含當前內(nèi)部類的外部類的實例,我們可以自動訪問其Java類。 現(xiàn)在,我們可以按上述步驟進行操作,并在封閉的類中掃描通用定義,然后將其添加到地圖中。 但是,MyGenericOuterClass中U的類型變量表示形式將不等于MyGenericInnerClass中U的類型表示形式。 就我們所知,MyGenericInnerClass可以是靜態(tài)的,并定義自己的通用變量名稱空間。 因此,任何表示Java API中通用變量的TypeVariable類型都配備了genericDeclaration屬性。 如果在不同的類中定義了兩個泛型變量,則即使它們在同一個名稱空間中共享一個名稱,而另一類是另一類的非靜態(tài)內(nèi)部類,則TypeVariable表示形式的定義也不相等。

因此,我們必須執(zhí)行以下操作:

  • 首先,嘗試在內(nèi)部類超類層次結(jié)構(gòu)中找到泛型類型。 就像處理非嵌套類一樣。
  • 如果無法解析類型:對于(非靜態(tài))內(nèi)部類及其所有外部類,請盡可能完整地解析類型變量。 這可以通過相同的extractTypeArguments算法來實現(xiàn),對于每個嵌套類,基本上為1 .。 我們可以通過檢查this $ 0字段是否為內(nèi)部類定義來獲取外部類。
  • 檢查外部類之一是否包含具有相同變量名的通用變量的定義。 在這種情況下,您找到了所需的通用變量的實際類型。
  • 在代碼中,如下所示:

    public static Class<?> findSubClassParameterType(Object instance, Class<?> classOfInterest, int parameterIndex) {Map<Type, Type> typeMap = new HashMap<Type, Type>();Class<?> instanceClass = instance.getClass();while (classOfInterest != instanceClass.getSuperclass()) {extractTypeArguments(typeMap, instanceClass);instanceClass = instanceClass.getSuperclass();if (instanceClass == null) throw new IllegalArgumentException();}ParameterizedType parameterizedType = (ParameterizedType) instanceClass.getGenericSuperclass();Type actualType = parameterizedType.getActualTypeArguments()[parameterIndex];if (typeMap.containsKey(actualType)) {actualType = typeMap.get(actualType);}if (actualType instanceof Class) {return (Class<?>) actualType;} else if (actualType instanceof TypeVariable) {return browseNestedTypes(instance, (TypeVariable<?>) actualType);} else {throw new IllegalArgumentException();} }private static Class<?> browseNestedTypes(Object instance, TypeVariable<?> actualType) {Class<?> instanceClass = instance.getClass();List<Class<?>> nestedOuterTypes = new LinkedList<Class<?>>();for (Class<?> enclosingClass = instanceClass.getEnclosingClass();enclosingClass != null;enclosingClass = enclosingClass.getEnclosingClass()) {try {Field this$0 = instanceClass.getDeclaredField("this$0");Object outerInstance = this$0.get(instance);Class<?> outerClass = outerInstance.getClass();nestedOuterTypes.add(outerClass);Map<Type, Type> outerTypeMap = new HashMap<Type, Type>();extractTypeArguments(outerTypeMap, outerClass);for (Map.Entry<Type, Type> entry : outerTypeMap.entrySet()) {if (!(entry.getKey() instanceof TypeVariable)) {continue;}TypeVariable<?> foundType = (TypeVariable<?>) entry.getKey();if (foundType.getName().equals(actualType.getName())&& isInnerClass(foundType.getGenericDeclaration(), actualType.getGenericDeclaration())) {if (entry.getValue() instanceof Class) {return (Class<?>) entry.getValue();}actualType = (TypeVariable<?>) entry.getValue();}}} catch (NoSuchFieldException e) { /* this should never happen */ } catch (IllegalAccessException e) { /* this might happen */}}throw new IllegalArgumentException(); }private static boolean isInnerClass(GenericDeclaration outerDeclaration, GenericDeclaration innerDeclaration) {if (!(outerDeclaration instanceof Class) || !(innerDeclaration instanceof Class)) {throw new IllegalArgumentException();}Class<?> outerClass = (Class<?>) outerDeclaration;Class<?> innerClass = (Class<?>) innerDeclaration;while ((innerClass = innerClass.getEnclosingClass()) != null) {if (innerClass == outerClass) {return true;}}return false; }

    哇,真丑! 但是上面的代碼使findSubClassParameterType甚至可以用于嵌套類。 我們可以進一步詳細介紹,因為我們還可以找到通用接口,通用方法,字段或數(shù)組的類型。 但是,所有此類提取的想法都相同。 如果子類知道其父類的泛型參數(shù),則可以通過反射來獲取它們。 否則,由于類型擦除,泛型參數(shù)將在運行時丟失而無法挽回。

    但是最后,這有什么好處? 對于許多開發(fā)人員而言,這傳達了執(zhí)行黑魔法的印象,因此他們寧愿避免編寫此類代碼。 誠然,通常有更簡單的方法來執(zhí)行這種查詢。 我們可以這樣定義MyGenericSubclass:

    class MyGenericClass<T> {private final Class<T> clazz;public MyGenericClass(Class<T> clazz) {this.clazz = clazz;}public Class<T> getGenericClass() {return clazz;} }

    當然,這也很好,甚至是更少的代碼。 但是,當您編寫供其他開發(fā)人員使用的API時,您通常希望它們盡可能的小巧和簡單。 (這可以從編寫大型框架到以兩個為一組的形式編寫軟件。)通過上述實現(xiàn),您可以迫使班級用戶提供可以通過不同方式檢索的冗余信息。 同樣,這種方法對于您暗中要求實現(xiàn)類添加相應構(gòu)造函數(shù)的接口也不太可行。 當著眼于Java 8及其功能接口 (也稱為閉包或lambda表達式)時,此問題將變得更加重要。 如果您需要通用接口除了提供其功能方法之外還提供getGenericClass方法,則不能再在lambda表達式中使用它們。

    PS:在寫這篇博客文章時,我破解了此代碼,但從未真正測試過,而是通過dupa調(diào)試 。 如果您需要這種功能,那么可以使用一個出色的名為gentyref的庫,它提供了上述分析以及更多內(nèi)容。

    參考: 高級Java泛型:從My每日Java博客中的JCG合作伙伴 Rafael Winterhalter 檢索泛型類型參數(shù) 。

    翻譯自: https://www.javacodegeeks.com/2013/12/advanced-java-generics-retreiving-generic-type-arguments.html

    java實現(xiàn)泛型檢索

    總結(jié)

    以上是生活随笔為你收集整理的java实现泛型检索_高级Java泛型:检索泛型类型参数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。