簡體   English   中英

當參數化類型通過層次結構傳遞時,在Java中創建泛型類型的實例嗎?

[英]Create instance of generic type in Java when parameterized type passes through hierarchies?

我一直在閱讀問題的答案:

在Java中創建泛型類型的實例?

我已經實施了Lars Bohl建議的方法。 我修改了他的代碼,如下所示:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class ParameterizedTypeEg<E> {
    public Class<E> getTypeParameterClass() {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }
    private static class StringHome extends ParameterizedTypeEg<String> {
        private String _string;
        StringHome (String string) {
            _string = string;
        }
    }
    public static void main(String[] args)
        throws InstantiationException, IllegalAccessException {
        String str = new StringHome("my string").getTypeParameterClass().newInstance();
        String str2 = new ParameterizedTypeEg<String>().getTypeParameterClass().newInstance();
    }
}

這種方法對於str變量很好用。 然后創建str2,在我看來它是相同的類型(ParameterizedTypeEg <String>,與StringHome基本相同)。 但是,該方法不適用於str2,並且在嘗試轉換(ParameterizedType)類型時會引發ClassCastException。

即使對於str2,我已經使用String參數化了ParameterizedTypeEg,但getGenericSuperclass()返回的內容與str截然不同。 同樣,在方法中,str2將“ this”顯示為ParameterizedTypeEg,而對於str,“ this”顯示為ParameterizedTypeEg $ StringHome。 我想這是問題的根源。 為什么Java不會也為str2確定了泛型類型?

當參數化類型通過多層層次結構傳遞時,我遇到了似乎同樣的問題? 也就是說,類B <T>包含A <T>,並且我實例化了一個B。在A中,我無法通過使用上述方法確定A的參數化類型來創建String對象。 在包含層次結構的情況下,該方法也會產生異常。 這給我帶來了一個問題,因為我希望能夠通過多個級別的包含和/或繼承傳遞參數化類型,並在所有情況下都采用相同的方法生成泛型類型的實例。

謝謝,約翰

進行以下更改,您的代碼將起作用:

  String str2 = new ParameterizedTypeEg<String>().getTypeParameterClass().newInstance();

   String str2 = new ParameterizedTypeEg<String>(){}.getTypeParameterClass().newInstance();

這將創建ParameterizedTypeEg的匿名子類。 你打電話時

getClass().getGenericSuperclass();

在StringHome上,您將獲得所需的ParameterizedTypeEg <java.lang.String>。 如果像您那樣創建str2,則該調用僅返回Object,因此將其強制轉換為參數化類型的嘗試失敗:

線程“主”中的異常java.lang.ClassCastException:無法將java.lang.Class強制轉換為java.lang.reflect.ParameterizedType

創建匿名子類將使此返回ParameterizedTypeEg <java.lang.String>

這與Google Guice Guave庫(btw)的Type Token類中使用的技巧相同。 你寫,例如

new TypeToken<List<String>>() {}

並不是

 new TypeToken<List<String>>()  

getClass().getGenericSuperclass(); 為您提供超類的詳細信息。 因此,只有在您將參數化超類作為子類時,它才起作用。 如果實例化給定類型參數的參數化超類,則它將無法正常工作。

但是,該方法不適用於str2,並且當我嘗試強制轉換(ParameterizedType)類型時拋出ClassCastException
...........為什么Java也看不到為str2確定了泛型?

如oracle doc中為ParameterizedType所指定:

ParameterizedType表示參數化類型,例如Collection<String>

但是, ParameterizedTypeEg超類是Object,而不是泛型類型。 因此,由getClass().getGenericSuperclass();返回的Type getClass().getGenericSuperclass(); 是對象本身的Class 將非參數化類型的類型強制轉換為ParameterizedType可以為str提供ClassCastException

為什么Java不會也為str2確定了泛型類型?

運行時的對象沒有任何類型參數信息。 基本上,要執行.newInstance()創建對象,您需要在運行時擁有類對象,這意味着您必須將其存儲在某處以獲取它。

它對str起作用的原因恰好是因為String類型存儲在類元數據中的某個位置。 類具有元數據,其中包括超類和超接口,如果是內部類則將其封閉,字段和方法的類型(方法的返回類型和參數類型)。 所有這些信息都與泛型一起在運行時存儲在字節碼中(其原因是Java中的文件可以單獨編譯,因此在編譯一個使用已編譯文件中的類的文件時,編譯器必須能夠查看類文件並查看泛型信息),並且可以在運行時通過對類對象的反射獲得。

因此,基本上, StringHome類充當String類對象的“存儲”,因為在編譯時將String硬編碼為StringHome類的類型參數。 因此,在運行時,您可以從此“存儲”中獲取它。

也就是說,類B <T>包含A <T>,並且我實例化了一個B。在A中,我無法通過使用上述方法確定A的參數化類型來創建String對象。

從上面的討論中,您可能會發現關鍵是要在運行時以某種方式“獲取” T類的類對象。 創建子類A<T>類將無濟於事,因為這樣做的目的是,實際類在編譯時進行了硬編碼,這需要在編譯時知道該類。 我們不知道在編譯時T是什么類,因此我們不能將其放入元數據中。 我們只能輸入“ T”,即類型變量。

因此,問題又回到了需要為type參數表示的類找到某種方式來傳遞或傳輸類對象。 您不能再依賴任何基於編譯時的機制。 因此,您必須將其傳遞給其他參數或其他參數。

暫無
暫無

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

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