国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java泛型進(jìn)階 - 如何取出泛型類型參數(shù)

linkFly / 1935人閱讀

摘要:然而,與普遍印象相反的是,某些情況下在運(yùn)行時(shí)獲取到泛型類型信息也是可行的。于是,編譯器可以把這部分泛型信息父類的泛型參數(shù)是,存儲(chǔ)在它的子類的字節(jié)碼區(qū)域中。當(dāng)使用反射取出中的類型參數(shù)時(shí),就必須把這點(diǎn)納入考量。獲取嵌套類的泛型的代碼如下

在JDK5引入了泛型特性之后,她迅速地成為Java編程中不可或缺的元素。然而,就跟泛型乍一看似乎非常容易一樣,許多開(kāi)發(fā)者也非常容易就迷失在這項(xiàng)特性里。
多數(shù)Java開(kāi)發(fā)者都會(huì)注意到Java編譯器類型擦除實(shí)現(xiàn)方式,Type Erasure會(huì)導(dǎo)致關(guān)于某個(gè)Class的所有泛型信息都會(huì)在源代碼編譯時(shí)消失掉。在一個(gè)Java應(yīng)用中,可以認(rèn)為所有的泛型實(shí)現(xiàn)類,都共享同一個(gè)基礎(chǔ)類(注意與繼承區(qū)分開(kāi)來(lái))。這是為了兼容JDK5之前的所有JDK版本,就是人們經(jīng)常說(shuō)的向后兼容性

向后兼容性
譯者注:原文較為瑣碎,大致意思是。在JVM整個(gè)內(nèi)存空間中,只會(huì)存在一個(gè)ArrayList.class
為了能夠區(qū)分ArrayListArrayList,現(xiàn)在假想的實(shí)現(xiàn)方式是在Class文件信息表(函數(shù)表+字段表)里添加額外的泛型信息。這個(gè)時(shí)候JVM的內(nèi)存空間中就會(huì)存在(假設(shè))ArrayList&String.class(假設(shè))ArrayList&Integer.class文件。順著這種情況延續(xù)下去的話,就必須要修改JDK5之前所有版本的JVM對(duì)Class文件的識(shí)別邏輯,因?yàn)樗茐牧?b>JVM內(nèi)部一個(gè)Class只對(duì)應(yīng)唯一一個(gè).class這條規(guī)則。這也是人們常說(shuō)的: 破壞了向后兼容性

注:參考Python3舍棄掉Python2的例子,也是放棄了對(duì)2的兼容,Python3才能發(fā)展并構(gòu)造更多的新特性。

那應(yīng)該怎么做?

既然Java開(kāi)發(fā)團(tuán)隊(duì)選擇了兼容JDK5之前的版本,那就不能在JVM里做手腳了。但Java編譯器的代碼似乎還是可以修改的。于是,Java編譯器編譯時(shí)就會(huì)把泛型信息都擦除,所以以下的比較在JVM運(yùn)行時(shí)會(huì)永遠(yuǎn)為真。

assert new ArrayList().getClass() == new ArrayList().getClass();

對(duì)JVM運(yùn)行時(shí)來(lái)說(shuō),上述代碼等同于

assert new ArrayList.class == ArrayList.class

到目前為止,上述內(nèi)容都是大家所熟知的事情。然而,與普遍印象相反的是,某些情況下在運(yùn)行時(shí)獲取到泛型類型信息也是可行的。舉個(gè)栗子:

class MyGenericClass { }
class MyStringSubClass extends MyGenericClass { }

MyStringSubClass相當(dāng)于對(duì)MyGenericClass做了類型參數(shù)賦值T = String。于是,Java編譯器可以把這部分泛型信息(父類MyGenericClass的泛型參數(shù)是String),存儲(chǔ)在它的子類MyStringSubClass的字節(jié)碼區(qū)域中。
而且因?yàn)檫@部分泛型信息在被編譯后,僅僅被存儲(chǔ)在被老版JVM所忽略的字節(jié)碼區(qū)域中,所以這種方式并沒(méi)有破壞向后兼容性。與此同時(shí),因?yàn)?strong>T已經(jīng)被賦值為String,所有的MyStringSubClass類的對(duì)象實(shí)例仍然共享同一個(gè)MyStringSubClass.class

如何獲取這塊泛型信息?

應(yīng)該如何獲取到被存儲(chǔ)在byte code區(qū)域的這塊泛型信息呢?

Java API提供了Class.getGenericSuperClass()方法,來(lái)取出一個(gè)Type類型的實(shí)例

如果直接父類的實(shí)際類型就是泛型類型的話,那取出的Type類型實(shí)例就可以被顯示地轉(zhuǎn)換為ParameterizeType

(Type只是一個(gè)標(biāo)記型接口,它里面僅包含一個(gè)方法:getTypeName()。所以取出的實(shí)例的實(shí)際類型會(huì)是ParameterizedTypeImpl,但不應(yīng)直接暴露實(shí)際類型,應(yīng)一直暴露Type接口)。

感謝ParameterizedType接口,現(xiàn)在我們可以直接調(diào)用ParameterizeType.getActualTypeArguments()取出又一個(gè)Type類型實(shí)例數(shù)組

父類所有的泛型類型參數(shù)都會(huì)被包含在這個(gè)數(shù)組里,并且以被聲明的順序放在數(shù)組對(duì)應(yīng)的下標(biāo)中。

當(dāng)數(shù)組中的類型參數(shù)為非泛型類型時(shí),我們就可以簡(jiǎn)單地把它顯示轉(zhuǎn)換為Class

為了保持文章的簡(jiǎn)潔性,我們跳過(guò)了GenericArrayType的情況。

現(xiàn)在我們可以使用以上知識(shí)編寫(xiě)一個(gè)工具類了:

public static Class findSuperClassParameterType(Object instance, Class clazzOfInterest, int parameterIndex) {
    Class subClass = instance.getClass();
    while (subClass.getSuperclass() != clazzOfInterest) {
        subClass = subClass.getSuperclass();
        if (subClass == null) throw new IllegalArgumentException();
    }
    ParameterizedType pt = (ParameterizedType) (subClass.getGenericSuperclass());
    return (Class) pt.getActualTypeArguments()[parameterIndex];
}

public static void testCase1() {
    Class genericType = findDirectSuperClassParameterType(new MyStringSubClass());
    System.out.println(genericType);
    assert genericType == String.class;
}

然而,請(qǐng)注意到

findSuperClassParamerterType(new MyGenericClass(), MyGenericClass.class, 0)

這樣調(diào)用會(huì)拋出IllegalArgumentException異常。之前說(shuō)過(guò):泛型信息只有在子類的幫助下才能被取出。然而,MyGenericClass只是一個(gè)擁有泛型參數(shù)的類,并不是MyGenericClass.class的子類。沒(méi)有顯式的子類,就沒(méi)有地方存儲(chǔ)String類型參數(shù)。因此上述調(diào)用不可避免地會(huì)被Java編譯器進(jìn)行類型擦除。如果你已預(yù)見(jiàn)到你的項(xiàng)目中會(huì)出現(xiàn)這種情況,也想要避免它,一種良好的編程實(shí)踐是將MyGenericClass聲明為abstract

然而,我們還沒(méi)有解決問(wèn)題,畢竟我們目前為止還有許多坑沒(méi)有填。

鏈?zhǔn)椒盒?/b>
class MyGenericClass {}
class MyGenericSubClass extends MyGenericClass {}
class MyStringSubSubClass extends MyGenericSubClass {}

如下調(diào)用,仍然會(huì)拋出異常。

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

這又是為什么呢?到目前為止我們都在設(shè)想:MyGenericClass的類型參數(shù)T的相關(guān)信息會(huì)存儲(chǔ)在它的直接子類中。那么上述的類繼承關(guān)系就有以下邏輯:

MyStringSubClass.class中存儲(chǔ)了MyGenericSubClass --> U = String

MyGenericSubClass.class中僅存儲(chǔ)了MyGenericClass --> T = U

U并不是一個(gè)Class類型,而是TypeVariable類型的類型變量,如果我們想要解析這種繼承關(guān)系,就必須解析它們之間所有的依賴關(guān)系。代碼如下:

public static Class findSubClassParameterType(Object instance, Class classOfInterest, int parameterIndex) {
    Map typeMap = new HashMap<>();
    Class instanceClass = instance.getClass();
    while (instanceClass.getSuperclass() != classOfInterest) {
        extractTypeArguments(typeMap, instanceClass);
        instanceClass = instanceClass.getSuperclass();
        if (instanceClass == null) throw new IllegalArgumentException();
    }
    // System.out.println(typeMap);
    ParameterizedType pt = (ParameterizedType) instanceClass.getGenericSuperclass();
    Type actualType = pt.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 typeMap, Class clazz) {
    Type genericSuperclass = clazz.getGenericSuperclass();
    if (!(genericSuperclass instanceof ParameterizedType)) {
        return ;
    }
    ParameterizedType pt = (ParameterizedType) genericSuperclass;
    Type[] typeParameters = ((Class) pt.getRawType()).getTypeParameters();
    Type[] actualTypeArguments = pt.getActualTypeArguments();
    for (int i = 0; i < typeParameters.length; i++) {
        if (typeMap.containsKey(actualTypeArguments[i])) {
            actualTypeArguments[i] = typeMap.get(actualTypeArguments[i]);
        }
        typeMap.put(typeParameters[i], actualTypeArguments[i]);
    }
}

代碼中通過(guò)一個(gè)map可以解析所有鏈?zhǔn)椒盒皖愋?/b>的定義。不過(guò)仍然不夠完美,畢竟MyClass extends MyOtherClass也是一種完全合法的子類定義。

嵌套類

好了好了,仍然沒(méi)有結(jié)束:

class MyGenericOuterClass {
  public class MyGenericInnerClass { }
}
class MyStringOuterSubClass extends MyGenericOuterClass { }
  
MyStringOuterSubClass.MyGenericInnerClass inner = new MyStringOuterSubClass().new MyGenericInnerClass();

下面這樣調(diào)用仍然會(huì)失敗。

findSuperClassParameterType(inner, MyGenericInnerClass.class, 0);

這種失敗幾乎是可預(yù)見(jiàn)的,我們正試圖在MyGenericInnerClass的對(duì)象實(shí)例里面尋找MyGenericInnerClass的泛型信息。就像之前所說(shuō),因?yàn)?b>MyGenericInnerClass并沒(méi)有子類,所以從MyGenericInnerClass.class中尋找泛型信息是不可能的,畢竟MyGenericInnerClass.class里面根本就不存在泛型信息。不過(guò)在這個(gè)例子中,我們檢查的是MyStringOuterSubClass中的非static內(nèi)部類: MyGenericInnerClass的對(duì)象實(shí)例。那么,MyStringOuterSubClass是知道它的父類MyGennericOuterClass --> U = String。當(dāng)使用反射取出MyGenericInnerClass中的類型參數(shù)時(shí),就必須把這點(diǎn)納入考量。

現(xiàn)在這件事就變得相當(dāng)棘手了。
-> 為了取出MyGenericOuterClass的泛型信息
-> 就必須先得到MyGenericOuterClass.class

這依然可以通過(guò)反射取得,Java編譯器會(huì)在內(nèi)部類MyGenericInnerClass中生成一個(gè)synthetic-field: this$0,這個(gè)字段可以通過(guò)Class.getDeclaredField("this$0")獲取到。

> javap -p -v MyGenericOuterClass$MyGenericInnerClass.class
...
...
  final cn.local.test.MyGenericOuterClass this$0;
    descriptor: Lcn/local/test/MyGenericOuterClass;
    flags: ACC_FINAL, ACC_SYNTHETIC
...

既然已經(jīng)有辦法可以獲取到MyGenericOuterClass.class了,那接下來(lái)我們似乎可以直接復(fù)用之前的掃描邏輯了。

這里需要注意, MyGenericOuterClass的U 并不等同于 的U
我們可以做以下推理,MyGenericInnerClass是可以聲明為static的,這就意味著static情況下,MyGenericInnerClass擁有它自己獨(dú)享的泛型type命名空間。所以,Java API中所有的TypeVariable接口實(shí)現(xiàn)類,都擁有一個(gè)屬性叫genericDeclaration


如果兩個(gè)泛型變量被分別定義在不同的類中,那么這兩個(gè)TypeVariable類型變量,從genericDeclaration的定義上來(lái)說(shuō)就是不相等的。

獲取嵌套類的泛型的代碼如下:

private static Class browseNestedTypes(Object instance, TypeVariable actualType) {
    Class instanceClass = instance.getClass();
    List> nestedOuterTypes = new LinkedList>();
    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 outerTypeMap = new HashMap<>();
            extractTypeArguments(outerTypeMap, outerClass);
            for (Map.Entry 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) {
            /* however, 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;
}

private static void extractTypeArguments(Map typeMap, Class clazz) {
    Type genericSuperclass = clazz.getGenericSuperclass();
    if (!(genericSuperclass instanceof ParameterizedType)) {
        return;
    }
    ParameterizedType pt = (ParameterizedType) genericSuperclass;
    Type[] typeParameters = ((Class) pt.getRawType()).getTypeParameters();
    Type[] actualTypeArguments = pt.getActualTypeArguments();
    for (int i = 0; i < typeParameters.length; i++) {
        if (typeMap.containsKey(actualTypeArguments[i])) {
            actualTypeArguments[i] = typeMap.get(actualTypeArguments[i]);
        }
        typeMap.put(typeParameters[i], actualTypeArguments[i]);
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/73445.html

相關(guān)文章

  • Java泛型類型擦除

    博客地址:Java泛型:類型擦除 前情回顧 Java泛型:泛型類、泛型接口和泛型方法 類型擦除 代碼片段一 Class c1 = new ArrayList().getClass(); Class c2 = new ArrayList().getClass(); System.out.println(c1 == c2); /* Output true */ 顯然在平時(shí)使用中,ArrayList...

    Hanks10100 評(píng)論0 收藏0
  • java的集合和泛型的知識(shí)點(diǎn)歸納1

    摘要:接口也是集合中的一員,但它與接口有所不同,接口與接口主要用于存儲(chǔ)元素,而主要用于迭代訪問(wèn)即遍歷中的元素,因此對(duì)象也被稱為迭代器。迭代器的實(shí)現(xiàn)原理我們?cè)谥鞍咐呀?jīng)完成了遍歷集合的整個(gè)過(guò)程。 【Collection、泛型】 主要內(nèi)容 Collection集合 迭代器 增強(qiáng)for 泛型 教學(xué)目標(biāo) [ ] 能夠說(shuō)出集合與數(shù)組的區(qū)別 [ ] 說(shuō)出Collection集合的常用功能 [ ]...

    daryl 評(píng)論0 收藏0
  • Java隨筆-Java泛型的一點(diǎn)學(xué)習(xí)

    摘要:以上代碼編譯通過(guò),運(yùn)行通過(guò)引入泛型的同時(shí),也為了兼容之前的類庫(kù),開(kāi)始引入的其實(shí)是偽泛型,在生成的字節(jié)碼中是不包含泛型中的類型信息的。進(jìn)行類型擦除后,類型參數(shù)原始類型就是擦除去了泛型信息,最后在字節(jié)碼中的類型變量的真正類型。 Java泛型 Java泛型(generics)是JDK 5中引入的一個(gè)新特性,允許在定義類和接口的時(shí)候使用類型參數(shù)(type parameter)。聲明的類型參數(shù)在...

    codeGoogle 評(píng)論0 收藏0
  • Java013-集合

    摘要:集合框架重點(diǎn)理解用于存儲(chǔ)數(shù)據(jù)的容器。集合容器在不斷向上抽取過(guò)程中。出現(xiàn)了集合體系。,刪除將集合中的元素全刪除,清空集合。刪除集合中指定的對(duì)象。注意刪除成功,集合的長(zhǎng)度會(huì)改變。作用用于取集合中的元素。是集合特有的迭代器。是單列集合是雙列集合 集合框架(重點(diǎn)理解):用于存儲(chǔ)數(shù)據(jù)的容器。特點(diǎn):1:對(duì)象封裝數(shù)據(jù),對(duì)象多了也需要存儲(chǔ)。集合用于存儲(chǔ)對(duì)象。2:對(duì)象的個(gè)數(shù)確定可以使用數(shù)組,但是不確定怎...

    qpal 評(píng)論0 收藏0
  • JAVA泛型筆記

    摘要:泛型類泛型類和普通類的區(qū)別就是類定義時(shí),在類名后加上泛型聲明。泛型類的內(nèi)部成員方法就可以使用聲明的參數(shù)類型。 泛型是JDK 1.5的一項(xiàng)新特性,它的本質(zhì)是參數(shù)化類型(Parameterized Type),即所操作的數(shù)據(jù)類型在定義時(shí)被指定為一個(gè)參數(shù)。當(dāng)我們使用的時(shí)候給這個(gè)參數(shù)指定不同的對(duì)象類型,就可以處理不同的對(duì)象。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口和...

    n7then 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<