2013-09-01 43 views
1

我一直在讀的問題的答案:當參數化類型通過層次結構時,在Java中創建泛型類型的實例?

Create instance of generic type in Java?

我已經實現了由拉爾斯·博爾建議的方法。我改編他的代碼如下:

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類包含A> < T>,並且我實例化了一個B.在A中,我不能通過使用上述方法確定A的參數化類型來創建一個String對象。該方法也會在遏制層次結構中產生異常。這使我產生了一個問題,因爲我希望能夠通過多級遏制和/或繼承來傳遞參數化類型,並且在所有情況下都有相同的方法生成泛型類型的實例。

感謝, 約翰

回答

1

用下面的改變,你的代碼將工作:

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

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

這將創建ParameterizedTypeEg的匿名子類。當您在StringHome調用

getClass().getGenericSuperclass(); 

,你會得到一個ParameterizedTypeEg < java.lang.String中>,這是你想要的。如果創建STR2像你一樣,該呼叫只是返回的對象,所以它轉換爲一個paremeterized類型的嘗試失敗:

異常在線程「主要」 java.lang.ClassCastException: 的java.lang.Class不能轉換爲java.lang.reflect.ParameterizedType

創建一個匿名子類使得返回ParameterizedTypeEg < java.lang。字符串>

這是在Google Guice Guave庫中的Type Token類中使用的相同技巧,順便說一句。你寫,例如

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

,而不是

new TypeToken<List<String>>() 
+0

謝謝。這工作。但是在處理遏制問題時我提到了一個類似的問題,並且我發佈了一個與此類似的新問題。請參閱http://stackoverflow.com/questions/18559285/create-instance-of-generic-type-in​​-java-when-parameterized-type-is-contained – Jorocco

0

getClass().getGenericSuperclass();給你超類的細節。因此,它只會在您參數化的超類的子類中起作用。如果您在給定類型參數的情況下實例化參數化超類,它將不起作用。

+0

你能否推薦另一種方法來創建泛型類型的實例,該實例可用於實例化提供類型參數的參數化超類? – Jorocco

+0

由於泛型是通過類型擦除實現的,我不認爲你可以在運行時決定價值。可能是你可以引入像'ParameterizedTypeEg(Class clazz)'的構造函數並強制將該類作爲參數傳遞 –

0

然而,這種方法不適用於STR2工作,當我嘗試投(ParameterizedType)型
一個ClassCastException 被拋出。 ..........爲什麼 Java沒有看到通用類型已經確定了str2 也?

在Oracle的文檔中指定爲ParameterizedType

ParameterizedType表示作爲 Collection<String>

一個參數化的類型,例如但,超類的ParameterizedTypeEg是對象,這是不通用類型。所以,由getClass().getGenericSuperclass();返回的Type是Object本身的Class。並且將非參數化類型鑄造到ParameterizedType會給 str

0

爲什麼Java沒有看到通用類型已被確定爲 str2呢?

運行時的對象沒有任何類型參數信息。基本上,爲了讓你做.newInstance()來創建一個對象,你需要在運行時擁有這個類對象,這意味着你必須將它存儲在某個地方以獲得它。

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

因此,基本上,在StringHome類充當一個「存儲」爲String類對象,因爲String被硬編碼爲在編譯時其超類的類型參數。所以在運行時你可以把它從這個「存儲」中解放出來。

即,B類< T>包含< T>和我實例化一個B.在A, 我不能通過確定參數化的類型A,使用上述方法的 創建一個字符串對象。

從我上面的討論中,你可能會發現,關鍵是要以某種方式在運行時「獲取」類爲T的類對象。創建一個類A<T>的類將無濟於事,因爲重點在於實際的類在編譯時被硬編碼,這需要在編譯時瞭解該類。我們不知道編譯時012xx是什麼類,所以我們不能把它放到元數據中;我們只能放「T」,即一個類型變量。

所以,問題又一次需要找到某種方式來傳遞或傳輸類型參數表示的類的類對象。你不能再依賴任何基於編譯時間的機制。所以你必須通過一個額外的參數或其他東西來傳遞它。