2013-02-26 190 views
16

我見過類似的問題,但他們沒有太大的幫助。如何在Java中獲取類型變量的類型泛型

比如我有這個泛型類:

public class ContainerTest<T> 
{ 

    public void doSomething() 
    { 
     //I want here to determinate the Class of the type argument (In this case String) 
    } 
} 

,並使用這種容器類

public class TestCase 
{ 

    private ContainerTest<String> containerTest; 

    public void someMethod() 
    { 
     containerTest.doSomething(); 
    } 
} 

是否有可能確定的類型參數的類的方法DoSomething的另一類()沒有明確類型變量/字段或任何構造函數在ContainerTest類?

更新:的ContainerTest類

+0

'if(t instanceof String)'? – vikingsteve 2013-02-26 08:41:40

+0

我已經更新了這個問題 – ZeDonDino 2013-02-26 08:44:48

+0

你不能只將類類型作爲參數傳遞嗎? – 2013-02-26 08:47:05

回答

12

唯一的辦法改變了形式是對類存儲在一個實例變量,並要求其作爲構造函數的參數:

public class ContainerTest<T> 
{ 
    private Class<T> tClass; 
    public ContainerTest(Class<T> tClass) { 
     this.tCLass = tClass; 
    } 

    public void doSomething() 
    { 
     //access tClass here 
    } 
} 
+1

這正是我想要做的。 – 2013-02-26 09:02:30

+2

你確定這是唯一的方法嗎? – vach 2014-05-06 06:37:05

+0

除非你強制調用者繼承你的班級,否則。但它通常不是問題... – 2014-05-06 11:15:54

5

沒有「乾淨」從類中獲取泛型類型參數的方法。 相反,一種常見模式是將泛型類的類傳遞給構造函數,並將其作爲內部屬性對齊,如java.util.EnumMap實現中所做的那樣。

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java

public class ContainerTest<T> { 

    Class<T> type; 
    T t; 

    public ContainerTest(Class<T> type) { 
     this.type = type; 
    } 

    public void setT(T t) { 
     this.t = t; 
    } 

    public T getT() { 
     return t; 
    } 

    public void doSomething() { 
     //There you can use "type" property. 
    } 
} 
+0

你爲什麼要鏈接EnumMap? – ZeDonDino 2013-02-26 08:51:59

+0

這被稱爲「類型令牌」 – hertzsprung 2013-02-26 08:54:01

+1

@ZeDonDino我鏈接EnumMap以顯示即使OpenJDK在standrad實現中也使用這種模式。 – RenaudBlue 2013-02-26 10:08:43

2

號這是不可能的,因爲type erasure(類型參數被編譯爲Object +類型轉換)。如果你真的需要在運行時知道/強制這個類型,你可以存儲對一個Class對象的引用。

public class ContainerTest<T> { 
    private final Class<T> klass; 
    private final List<T> list = new ArrayList<T>(); 

    ContainerTest(Class<T> klass) { 
    this.klass = klass; 
    } 

    Class<T> getElementClass() { 
    return klass; 
    } 

    void add(T t) { 
     //klass.cast forces a runtime cast operation 
     list.add(klass.cast(t)); 
    } 
} 

用途:

ContainerTest<String> c = new ContainerTest<>(String.class); 
+2

請謹慎使用,僅在Java SE 7及更高版本中支持。 – ZeDonDino 2013-02-26 08:55:44

+0

ContainerTest c = new ContainerTest (String.class) //所以鑽石誕生了 – Javier 2013-02-26 08:57:29

+0

嗯,我知道這一點。我希望有一種方法可以將類從中取出,而不是將值傳遞給構造函數或字段或類似的方法。 – ZeDonDino 2013-02-26 09:04:34

0

如果你可以這樣定義

public class ContainerTest<T> 
{ 

    public void doSomething(T clazz) 
    { 

    } 
} 

那麼很可能

+0

我想在不帶任何參數的情況下調用此方法。 – ZeDonDino 2013-02-26 08:54:36

0

一種方式來獲得的運行時類型鍵入參數,使用Guava的TypeToken來捕獲它。該解決方案的缺點是,每次需要Container的實例時,您都必須創建一個匿名子類。

class Container<T> { 

    TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {}; 

    public Type getContainedType() { 
     return tokenOfContainedType.getType(); 
    } 
} 

class TestCase { 

    // note that containerTest is not a simple instance of Container, 
    // an anonymous subclass is created 
    private Container<String> containerTest = new Container<String>() {}; 

    @Test 
    public void test() { 
     Assert.assertEquals(String.class, containerTest.getContainedType()); 
    } 
} 

這個解決方案的關鍵是在上面的代碼中使用TypeToken的構造的THA JavaDoc的描述:

客戶創建一個空的匿名子類。這樣做會將類型參數嵌入到匿名類的類型層次結構中,這樣我們就可以在運行時重新構建它,而不會擦除。

+0

哇,一個downvote,這很好...你至少可以評論你爲什麼不喜歡它? – zagyi 2013-02-26 11:25:33

+0

這是一個有趣的解決方案,但我認爲它會更適合像番石榴那樣的反射,而不是這個問題的用例。這個解決方案的問題在於它需要在實例化它的每個位置創建一個匿名類。 (注意我不是downvoter) – 2013-02-26 12:38:00

8

如果你有興趣在反射方式,我發現這篇大文章中的部分解決方案:http://www.artima.com/weblogs/viewpost.jsp?thread=208860

總之,你可以使用java.lang.Class.getGenericSuperclass()java.lang.reflect.ParameterizedType.getActualTypeArguments()方法,但你必須繼承一些父超類。

以下代碼片段適用於類直接擴展超類AbstractUserType。請參閱參考文章以獲取更通用的解決方案

import java.lang.reflect.ParameterizedType; 


public class AbstractUserType<T> { 

    public Class<T> returnedClass() { 
     ParameterizedType parameterizedType = (ParameterizedType) getClass() 
       .getGenericSuperclass(); 

     @SuppressWarnings("unchecked") 
     Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0]; 

     return ret; 
    } 

    public static void main(String[] args) { 
     AbstractUserType<String> myVar = new AbstractUserType<String>() {}; 

     System.err.println(myVar.returnedClass()); 
    } 

} 
+0

這對我有效。謝謝。 – aBarocio80 2015-01-09 16:37:17

1

要了解的T價值,你需要通過繼承ContainerTest捕捉到它的類型定義:

class MyStringClass extends ContainerTest<String> {} 

您也可以與匿名類做到這一點,如果你想:

ContainerTest<String> myStringClass = new ContainerTest<String>{}(); 

然後解決T的值,你可以使用TypeTools

Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass()); 
assert stringType == String.class;