如何将Class对象用作参数化类型?

时间:2014-11-02 19:37:55

标签: java generics

因此Google提出了很多关于获取参数化类型的.class的问题,但我正试图采用其他方式。

我有一个类列表,我需要创建一个使用Class作为键的映射,以及一个类型为Class的ArrayList对象作为值。像这样:

Class[] classes = getArrayOfClasses();
HashMap<Class, ArrayList<?>> map = new HashMap<Class, ArrayList<?>>();
for(Class c : classes) {
    map.put(c, new ArrayList<c>());    // here is where the problem is
}

问题当然是它需要参数化类型,而不是类。一种可能的解决方法是只使用map.put(c, new ArrayList<Object>()),但是我必须知道类型并转换我拉的每个对象。

MyClass myObj = (MyClass) map.get(MyClass.class).get(0);

我也尝试过这样的初始化函数:

private <T> ArrayList<T> makeArrayList(Class<T> c) {
    return new ArrayList<T>();
}

这有我希望可以使用的语法,但它仍然留给我一个ArrayList Object必须被投射。

那么我有没有办法让ArrayList参数化为类的类型?

2 个答案:

答案 0 :(得分:2)

不幸的是,由于Java的泛型(它们通过擦除工作)的性质,泛型在运行时是不可用的。

这意味着没有真正的方法让map.put(c, new ArrayList<c>());工作。

我建议改为:将列表包装在专用对象中,并为该对象提供以下访问方法:

public <T> getList(Class<T> key) {
    List<?> list = map.get(key);
    return (List<T>) list;
}

这会产生警告,但只要地图构造得当,你就可以了。

或者,您可以对所有对象进行运行时检查,以确保类型匹配。

public <T> getList(Class<T> key) {
    List<?> list = map.get(key);
    for(Object o : list){
        assert(key.isInstance(o));
    }
    return (List<T>) list;
}

答案 1 :(得分:1)

编译后的字节码绝对没有区别:

new ArrayList();
new ArrayList<T>();
new ArrayList<String>();
new ArrayList<Integer>();

type参数对编译的代码没有影响;它只影响编译器的类型检查。因此,如果类型参数在编译时不可用,则它是无用的,因为编译时是唯一有用的时间。

你应该写new ArrayList<Object>()new ArrayList<Integer>()甚至new ArrayList<CompletelyBogusUnrelatedClass>();它并不重要,因为它们都与ArrayList<?>兼容,ArrayList是地图的值类型。

至于创建并返回private static <T> ArrayList<T> makeArrayList() { return new ArrayList<T>(); } 的方法,它应该像这样写:

ArrayList

(那是对的,这个方法返回你想要的任何元素类型的{{1}} ,甚至不知道那个类型是什么!这清楚地证明了类型参数在运行时不需要。)