2008-10-08 102 views
56

我正在創建一個泛型類,並且在我需要知道當前正在使用的泛型類的一個方法中。原因是我調用的方法之一期望這是一個參數。如何確定泛型的類?

例子:

public class MyGenericClass<T> { 
    public void doSomething() { 
    // Snip... 
    // Call to a 3rd party lib 
    T bean = (T)someObject.create(T.class); 
    // Snip... 
    } 
} 

顯然上面的例子不工作,導致以下錯誤:非法類文本的類型參數T.

我的問題是:沒有人知道一個良好替代或解決方法?

回答

45

仍存在同樣的問題:通用信息在運行時被擦除,無法恢復。一種解決方法是通過T級的靜態方法的參數:

public class MyGenericClass<T> { 

    private final Class<T> clazz; 

    public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) { 
     return new MyGenericClass<U>(clazz); 
    } 

    protected MyGenericClass(Class<T> clazz) { 
     this.clazz = clazz; 
    } 

    public void doSomething() { 
     T instance = clazz.newInstance(); 
    } 
} 

它的醜陋,但它的工作原理。

+0

它確實很醜,但帶保護構造函數的技巧對我來說已經足夠了。我使用泛型類作爲抽象類,用作基礎5到10個具體類。謝謝! – 2008-10-08 13:36:16

+0

是否可以使用它來確定對象是什麼泛型?例如 `if(obj.getClazz()是一個字符串)doThis();如果(obj.getClazz()是一個整數)doThat(); ' – dwjohnston 2012-11-09 00:09:36

+1

^解決方案: `if(obj.getClazz()。equals(String.class))...' – dwjohnston 2012-11-09 01:14:32

21

我只是指出了此解決方案:

import java.lang.reflect.ParameterizedType; 

public abstract class A<B> { 
    public Class<B> g() throws Exception { 
     ParameterizedType superclass = 
      (ParameterizedType) getClass().getGenericSuperclass(); 

     return (Class<B>) superclass.getActualTypeArguments()[0]; 
    } 
} 

如果A給出由子類中的具體類型此作品:

new A<String>() {}.g() // this will work 

class B extends A<String> {} 
new B().g() // this will work 

class C<T> extends A<T> {} 
new C<String>().g() // this will NOT work 
4

不幸的是克里斯托夫的解決方案,編寫只能在非常有限的情況下。 [編輯:如下面評論我不再記得我對這句話的推理,它可能是錯誤的:「請注意,這將只在抽象類,首先。」]下一個難點是,g()只適用於DIRECT子類A。我們可以修復,但:

private Class<?> extractClassFromType(Type t) throws ClassCastException { 
    if (t instanceof Class<?>) { 
     return (Class<?>)t; 
    } 
    return (Class<?>)((ParameterizedType)t).getRawType(); 
} 

public Class<B> g() throws ClassCastException { 
    Class<?> superClass = getClass(); // initial value 
    Type superType; 
    do { 
     superType = superClass.getGenericSuperclass(); 
     superClass = extractClassFromType(superType); 
    } while (! (superClass.equals(A.class))); 

    Type actualArg = ((ParameterizedType)superType).getActualTypeArguments()[0]; 
    return (Class<B>)extractClassFromType(actualArg); 
} 

這將在許多情況下在實踐中,但不是所有的時間。試想一下:

public class Foo<U,T extends Collection<?>> extends A<T> {} 

(new Foo<String,List<Object>>() {}).g(); 

,這將拋出一個ClassCastException,因爲類型參數在這裏不是ClassParameterizedType在所有;這是TypeVariableT。所以現在你會被卡住,試圖找出什麼類型的T應該代表,等等。

我認爲唯一合理的一般答案類似於Nicolas的初始答案 - 一般來說,如果您的類需要在編譯時實例化某些其他未知類的對象,那麼您的類的用戶需要通過該類的字面量(或者,也許是一個工廠)顯式地給你的類,而不是完全依賴於泛型。

2

我找到另一種方式來獲得通用對象的類

public Class<?> getGenericClass(){ 
     Class<?> result =null; 
     Type type =this.getClass().getGenericSuperclass(); 

     if(type instanceofParameterizedType){ 
       ParameterizedType pt =(ParameterizedType) type; 
       Type[] fieldArgTypes = pt.getActualTypeArguments(); 
       result =(Class<?>) fieldArgTypes[0]; 
     } 
     return result; 
    } 
0

T能夠很容易地使用TypeTools解決:

Class<T> t = (Class<T>) TypeResolver.resolveRawArguments(
           MyGenericClass.class, getClass()); 
0

我會闡述克里斯托夫的解決方案。

這裏是ClassGetter抽象類:

private abstract class ClassGetter<T> { 
    public final Class<T> get() { 
     final ParameterizedType superclass = (ParameterizedType) 
      getClass().getGenericSuperclass(); 
     return (Class<T>)superclass.getActualTypeArguments()[0]; 
    } 
} 

這裏是使用上面的類找到一個通用類類型的靜態方法:

public static <T> Class<T> getGenericClass() { 
    return new ClassGetter<T>() {}.get(); 
} 

作爲一個例子的是使用量,你可以這樣做:

public static final <T> T instantiate() { 
    final Class<T> clazz = getGenericClass(); 
    try { 
     return clazz.getConstructor((Class[])null).newInstance(null); 
    } catch (Exception e) { 
     return null; 
    } 
} 

然後像這樣使用它:

T var = instantiate();