想象一下,我们有一个工厂,通过它的类创建对象。问题是 - 如何为泛型类声明这样的工厂?例如,可以创建Map< K,V>的通用工厂。或列表< T>或任何其他类型。当然,< K,V>映射< K,V> create()不是答案。
如果不可能,我想从语言设计的角度来理解为什么。
编辑:正如Marko所说,这个问题实际上是关于编译时类型的安全性。我怎样才能宣布这样的工厂以避免警告和手工演员?示例:
import java.util.Map;
class Factory {
public static <T> T create(Class<T> clazz) {
return null;//some implementation here
}
}
class App {
public static void main(String[] args) {
//works perfectly
String s = Factory.create(String.class);
//Type safety: The expression of type Map needs unchecked conversion to conform to Map<String,Integer>
Map<String, Integer> map = Factory.create(Map.class);
//How can we specify that T is Map<String, Integer> ?
//Factory.<Map<String, Integer>>create(Map.class); - does not work
}
}
答案 0 :(得分:4)
类文字表达式仅表示原始类型。您不能事后参数化其引用对象,因为它是一个类型已经修复的常量。因此,您无法传输使编译器推断完全参数化泛型类型所需的类型参数信息。
适用于像Jackson这样的API的解决方法是拥有TypeReference
并通过创建指定了参数的实际子类来捕获所有类型参数,例如
new TypeReference<Map<String,Integer>>() {}
当您将此传递给create
方法时,编译器将能够推断其泛型类型参数。反射甚至可以让你在运行时检索参数化类型擦除并不像经常呈现的那样普遍。
答案 1 :(得分:0)
这是不可能的 - 原因是,泛型是通过擦除实现的,而不是通过具体化来实现的。这个的简短版本是“泛型在运行时不存在”;它们基本上只用于编译期间的类型检查。所以没有Map<String, Integer>.class
或Map<Long, List<String>>.class
这样的东西 - 只有对象Map.class
。
但从正面来看,由于擦除,对象也只能真正属于Map
类。因此,如果您通过反射构建原始类型Map.class
的对象,并将其强制转换为Map<String, Integer>
,那么 具有所有相同属性的Map<String, Integer>
仿佛它是以正常方式创建的。
警告是正确的,因为它说你正在断言编译器无法自动检查的通用边界。由于上述原因,这是不可避免的,并且在这种情况下不是问题。我会在这里添加评论,抑制警告并继续。我不知道有更好/更清洁的方式来实现你在这里做的事情。