2008-10-08 97 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();