2012-06-22 31 views
8

想象以下場景:對Java中泛型參數類的反思?

class MyClass extends OtherClass<String>{ 

    String myName; 
    //Whatever 

} 

class OtherClass<T> { 

    T myfield; 

} 

,我使用反射專門(MyClass.class).getDeclaredFields()分析MyClass的,在這種情況下,我會得到(領域和類型,使用的getType())以下字段:

myName --> String 
myField --> T 

我想獲得T的實際類型,由於擴展表示法中顯式的「字符串」,這在運行時已知,我該如何獲取非遺傳類型的myField?

編輯解決:

好像答案是「你不能」。對於那些稍後可能會看到這個問題的人,我建議使用Jackson(我試圖這樣做來生成JSON)並以這種方式註釋您的類和字段,以便Jackson知道繼承層次結構並可以自動執行建議下面的正確答案。

+1

答案不是你不能;您可以輕鬆地創建從類型參數到顯式類型參數的映射。 – Jeffrey

回答

18

只有在明確使用String時才能實現反射,否則由於類型擦除,此信息將會丟失。

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String> 
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String> 
+0

看起來你贏得了比賽的完整答案:) – Jochen

+0

這是一個很好的答案,但是我怎樣才能做到這一點與一個字段對象?我不想明確地要求超類,因爲我只是試圖獲得我的子類的所有聲明字段,其中一個恰好是通用的。我只想要類似於getDeclaredFields()的東西,但對泛型更聰明。那有意義嗎? –

+0

@hatboysam你需要一種方法或其他方法來請求超類。調用'Field.getGenericParameter'只會返回類型參數(在本例中爲'T'),而不是顯式類型參數('String')。你可以通過創建一個從MyClass.class.getSuperclass()。getTypeArguments()'到'MyClass.class.getGenericSuperclass()。getActualTypeArguments()'的映射來獲取顯式類型參數。 – Jeffrey

-4

由於Type Erasure,沒有直接的方法來獲取實際類型。但是,您可以使用以下方法:在你的OtherClass<T>

,寫出下列抽象方法:

protected abstract class<T> getClazz(); 

然後在MyClass,需要實現方法:

@Override 
protected Class<String> getClazz(){ 
    return String.class; 
} 

,那麼你可以調用getClazz()去上課。

+0

+1類型擦除鏈接 – BlackVegetable

+2

OP爲他的泛型類提供了一個具體的類型參數,該信息*在運行時保存並可以被檢索。 – Jeffrey

+0

-1 - 這個解決方案是不必要的,像在這篇文章中指出的其他解決方案。另外,你的'getClass()'會和最後一個方法['Object#getClass()']產生名稱衝突(http://docs.oracle.com/javase/7/docs/api/java/lang/Object .html#getClass \(\)) –

0

通用類型是而不是在運行時已知。只有編譯器知道它們,檢查你的程序是否輸入正確,然後刪​​除它們。

在您的具體情況下,調用MyClass.class.getGenericSuperclass()可能會爲您提供所需的信息,因爲出於某種奇怪的原因,繼承時使用的具體類型保存在類描述符中。

+2

這不是「一些奇怪的原因」。 *字段,方法,超類,封閉類等的聲明總是被保留下來,因爲其他類在編譯時需要使用它們。這是與沒有泛型的運行時類型對象分開的問題。 – newacct

3

我找到了一個不錯的解釋here

當運行時檢查一個參數化類型本身,比如java.util.List的,有沒有知道什麼類型已經對參數的方式。這是有道理的,因爲類型可以被參數化爲同一應用程序中的各種類型。但是,當您檢查聲明使用參數化類型的方法或字段時,您可以在運行時查看可參數化類型參數化的類型。

簡而言之:

你看不到一個類型本身它是什麼類型參數的運行時間,但你可以看到它的領域和方法,它被使用和參數。

在代碼:

你看不到T這裏:

class MyClass<T> { T myField; } 

您可以看到 「T」 在這裏:

class FooClass { 
    MyClass<? extends Serializable> fooField; 
} 

在這裏,您將能夠告訴型號和型號參數爲fooField。 參見getGeneric*()方法ClassMethod

順便說一句,我經常會看到這樣(縮短):

Class fieldArgClass = (Class) aType.getActualTypeArguments()[0]; 

這是不正確的,因爲getActualTypeArguments()可能,常會,返回TypeVariable而不是類的 - 這時候一般是<? extends SomeClass>代替只是<SomeClass>。它可以走得更深,試想:

class FooClass { 
    MyClass<? extends Map<String, List<? extends Serializable>>> fooField; 
} 

所以,你得到的Type秒的樹。但這有點偏離主題。享受:)

0

這是爲什麼反射不是一個好主意的典型例子。

通過反射可以從程序中獲得的內容僅僅是語言的編譯人員選擇提供的事實。

而且他們一般不能提供所有的東西;他們有點必須保持原始程序文本。

有關您的代碼的所有其他信息因此無法提供給reflectee。

的治癒,這是到步驟的語言和使用的工具,可以提供的有關代碼的信息的任何任意的比特。這些工具被稱爲Program Transformation Systems (PTS)

PTS解析源代碼並構建AST代表它。一個好的PTW將建立一個AST,它基本掌握關於代碼的一切(操作符,操作數,標點符號和註釋),以便它可以被檢查。通常情況下,PTS將記錄語言令牌的行/列位置,以便即使佈局信息可用;極端的PTS會將記號之間的空白記錄下來,或者至少知道如何在必要時閱讀原始文本文件。這個AST本質上相當於我說的必要的全文,但是以更方便的方式處理。 (PTSs還有一個非常不錯的屬性:他們可以修改 AST併爲修改後的程序重新生成代碼,但這超出了反思,所以我不會在這方面進一步發表評論)。