簡體   English   中英

Java泛型,類型擦除和泛型成員的類型

[英]Java generics, type erasure and type of a generic member

Java有類型擦除,人們說在運行時無法確定通用對象的類型而沒有黑客攻擊。 請考慮以下代碼

    public class TestClass<T> {

    private T genericField;

    public TestClass(T genericField) {
        this.genericField = genericField;
    }

    public void printTypeInfo() {
        System.out.println("Hi I'm a " + genericField.getClass());
        System.out.println("Am I a string? " + (genericField instanceof String));
        System.out.println("Am I a long? " + (genericField instanceof Long));
    }

    public static void main(String [] args) {
        TestClass<String> genericString = new TestClass<>("Hello");
        TestClass<Long> genericLong = new TestClass<>(111111L);
        genericString.printTypeInfo();
        System.out.println("------------------");
        genericLong.printTypeInfo();
    }
}

它給了我以下結果:

Hi I'm a class java.lang.String
Am I a string? true
Am I a long? false
------------------
Hi I'm a class java.lang.Long
Am I a string? false
Am I a long? true

似乎類型信息在運行時很容易獲得。 我在這里錯過了什么?

TestClass<Number> genericNumber = new TestClass<>(42L);
genericNumber.printTypeInfo();

這將打印Hi I'm a Long而不是Hi I'm a Number 您可以看到genericFieldLong但您無法看到T被實例化為Number

這是一個由於類型擦除而無法做的事情的例子。

TestClass<?> generic = new TestClass<String>("Hello");

if (generic instanceof TestClass<String>) {
    System.out.println("It holds a string!");
}
else if (generic instanceof TestClass<Long>) {
    System.out.println("It holds a long!");
}

您可以在運行時確定genericField中任何給定對象的類型,但是您無法在運行時確定TestClass<X>TestClass<Y>之間的差異,而無需檢查您知道碰巧受泛型類型約束的某些成員。 也就是說,如果僅給出一個TestClass實例,則無法確定TestClass<...>的類型參數。

您的代碼顯示genericField的值的類型, 而不是 TestClass實例的參數化類型。 嘗試打印this.getClass() ,你會發現它在兩種情況下都是相同的。

你“缺少”的是這樣的:你(可以理解)在genericField本身擁有一個對象(帶有一個類型)和TestClass有一個泛型類型參數這一事實之間建立了一個不正確的聯系。 您可以通過確定為TestClass指定的類型參數的能力來確定genericField值的類型。 也就是說,雖然您可以根據genericFieldT知識推斷出類型參數是什么,但這與直接確定T是什么不同,這是不可能的。

查看上一段的另一種方法是考慮以下幾點:

  • 如果TestClass沒有類型為T成員,則沒有其他方法可以提取T 您的代碼僅“確定” T基於您自己的個人知識,即genericField被聲明為持有相同類型(因此其中的對象必須屬於該類型,因此您可以得出結論通用參數可能是相同的)類型或某種超類型)。

  • 如果你沒有使用泛型,而genericField只是一個Object ,你仍然可以在genericField確定對象的類型。 也就是說,它的類型與泛型類型“獨立”,除非使用泛型,否則編譯器會對類型進行約束。 編譯后它仍然只是一個任意對象,無論你是否使用泛型(這實際上只是一種方便,因為你可以在沒有泛型的情況下完成所有這些,只需使用Object和大量的強制轉換)。

還要考慮TestClass<Base>的可能性,其中genericField被賦予了Derived 您的代碼將正確顯示genericFieldDerived ,但您無法知道類型參數是BaseDerived ,因為信息已被刪除。


此外,將以上幾點進一步歸結為家:

TestClass<String> genericString = new TestClass<String>("Hello");
TestClass<?> kludge = genericString;
TestClass<Long> genericLongButNotReally = (TestClass<Long>)kludge;

genericLongButNotReally.printTypeInfo();

輸出String的信息(這就是給出那些“未經檢查的轉換”警告的原因,以防止出現這種奇怪的事情),而不關心使用Long類型參數指定genericLongButNotReally的事實。 當您使用泛型類型時,必須克服編譯器提供的良好保護; 但在運行時它並不關心。

能夠獲得變量的類型和對象的類型是兩回事。

僅僅因為你可以得到genericField的類型並不意味着你可以看到T是一個數字。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM