假设我有以下方法,可用于创建指定类型的集合。
private static Collection<?> void create(Class<? extends Collection<?>> cls) {
return cls.newInstance();
}
如果在运行时传入cls参数,这一切都很好:
List<String> list = new LinkedList<String>();
create(list.getClass());
但是如何在没有未经检查的警告的情况下在代码中调用此方法?说我想做点什么:
create(LinkedList.class);
它会抱怨创建(Class)没有被定义,严格来说这是正确的,因为List不是Collection的#capture,但我该如何使它工作?
非常感谢!
答案 0 :(得分:3)
Neal Gafter谈论这个问题here。修复是使用超类型令牌。这是Guice用来维护通用类型信息的,我相信。
基本上,它是一个额外的类,可用于表示类型而不是Foo.class
。你会这样使用它:
TypeReference<LinkedList<String>> x = new TypeReference<LinkedList<String>>() {};
create(x);
请注意,尝试使用 raw 类型LinkedList
仍会导致编译器抱怨,故意这样 - 当您使用原始类型时,您基本上选择了类型安全。但是这种方案允许你以一种更难的方式表达泛型类型。
答案 1 :(得分:0)
我们这里有两个泛型类型,一个用于集合的子类型,一个用于集合的元素类型。以下代码可以工作:
private static <C extends Collection<E>, E>
C create(Class<C> cls) throws Exception
{
return cls.newInstance();
}
用法示例。没有警告。
Class<LinkedList<String>> clazz = ...;
LinkedList<String> result = create(clazz);
现在的问题是我们如何获得clazz
?你必须非常努力地说服编译器:
List<String> list = new LinkedList<String>();
clazz = (Class<LinkedList<String>>)list.getClass();
clazz = (Class<LinkedList<String>>)(Class<?>)LinkedList.class;
我们可以使用实用方法来简化类型转换:
public static <C extends Collection, E, T extends Collection<E>>
Class<T> type(Class<C> classC, Class<E> classE)
{
return (Class<T>)classC;
}
//usage:
clazz = type(LinkedList.class, String.class);
result = create(clazz);
有趣的是:
Class<LinkedList<LinkedList<String>>> clazz2;
LinkedList<LinkedList<String>> result2;
clazz2 = type(LinkedList.class, clazz);
result2 = create(clazz2);
但在某些时候,我们必须停止这场愚蠢的比赛。通用打字应该对我们有所帮助,而不是折磨我们。如果它变得太麻烦并且使我们的程序无法读取,那就放下它。使用我们多年来幸福生活的该死的原始类型。