如何获取参数化的类实例

9
自从引入了泛型,Class也被参数化了,因此List.class会产生Class<List>。这是很清楚的。
我无法弄清楚如何获得一个参数化类型的Class实例,即Class<List<String>>。就像在这个片段中:
```java List list = new ArrayList(); Class listClass = list.getClass(); ```
在这个例子中,listClass只是List类的Class对象,而不是参数化的Class对象。
public class GenTest {
    static <T> T instantiate(Class<T> clazz) throws Exception {
        return clazz.newInstance();
    }
    public static void main(String[] args) throws Exception {
        // Is there a way to avoid waring on the line below
        // without using  @SuppressWarnings("unchecked")?
        // ArrayList.class is Class<ArrayList>, but I would like to
        // pass in Class<ArrayList<String>>
        ArrayList<String> l = GenTest.instantiate(ArrayList.class);
    }
}

我经常遇到这个问题的变体,但我仍然不知道是否有更好的方法,或者我是不是漏掉了什么。谢谢您提供建议。

4个回答

8

Class类是一种类型的运行时表示。由于参数化类型在运行时会被擦除,因此Class类对象对于Class<List<Integer>>和Class<List<String>>是相同的。

你不能使用.class符号来实例化它们的原因是这是用于类字面量的特殊语法。Java语言规范明确禁止在类型为参数化的情况下使用该语法,这就是为什么List<String>.class不被允许的原因。


我完全清楚擦除背后的逻辑。问题纯粹在于如何将 T 与 ArrayList<String> 关联起来,以使方法的结果类型与分配给它的变量匹配 - 以使编译器满意而无任何警告。 - Michal
任何与类对象一起工作的代码都只能在类型擦除后访问类型。因此,如果您有一个List<Integer> intList,则可以调用instantiate(intList.getClass()),实际的Class对象将是List.class。 - Avi
很抱歉,@Avi,你的例子不起作用: List<String> stringList = Collections.<String>emptyList(); instantiate(stringList.getClass()); 会得到以下错误信息: 在类型中,方法instantiate(Class<List<String>>)不适用于参数(Class<capture#2-of ? extends List>)。 - thSoft

1

我认为你试图做的事情是不可能的。首先,你的实例化方法不知道它正在处理一个参数化类型(你可以轻松地传递java.util.Date.class)。其次,由于类型擦除,在运行时对参数化类型进行特别具体的操作是困难或不可能的。

如果你用不同的方式来解决这个问题,还有其他一些小技巧,比如类型推断:

public class GenTest
{
    private static <E> List<E> createList()
    {
        return new ArrayList<E>();
    }

    public static void main(String[] args)
    {
        List<String> list = createList();
        List<Integer> list2 = createList();
    }
}

1

类表示由类加载器加载的原始类型。要表示参数化类型,请使用java.lang.reflect.ParameterizedType。


你能否举个例子来详细说明一下,以帮助那些从未使用过java.lang.reflect.ParameterizedType的人? - Henry B
无论如何,ParameterizedType接口没有提供任何创建实例的方法... - Nicolas

0

你唯一能做的就是直接实例化 List<String> 并调用它的 getClass() 方法:

instantiate(new List<String>() { ... }.getClass());

对于具有多个抽象方法的类型,例如List,这是相当尴尬的。但不幸的是,调用子类构造函数(例如new ArrayList<String>)或工厂方法(Collections.<String>emptyList())都无法解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接