通用 类 和 Class<T>
Generic classes and Class<T>
假设我们有一个工厂,它通过 class 创建对象。问题是 - 如何为通用 classes 声明这样的工厂?例如,可以创建 Map 或 List 或任何其他类型的通用工厂。当然, Map 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
}
}
这是不可能的 - 原因是泛型是通过擦除而不是具体化实现的。这个的简短版本是"generics don't exist at runtime";它们本质上仅用于编译期间的类型检查。所以没有 Map<String, Integer>.class
或 Map<Long, List<String>>.class
这样的东西 - 只有对象 Map.class
.
但从好的方面来说,一个对象也只能真正是class Map
,因为擦除。因此,如果你通过反射构造一个原始类型 Map.class
的对象,并将其转换为 Map<String, Integer>
,那么它 是 一个 Map<String, Integer>
与所有与以正常方式创建的属性相同。
警告是正确的,因为它是说您正在断言编译器无法自动检查的通用边界。由于上述原因,这是不可避免的,在本例中不是问题。我会在这里添加评论,取消警告并继续。我不知道有什么 better/cleaner 方法可以实现您在这里所做的事情。
一个class文字表达式只表示原始类型。你不能 post-hoc 参数化它的引用对象,因为它是一个类型已经固定的常量。因此,您无法传递类型参数信息,您需要让编译器推断完全参数化的泛型类型。
适用于 Jackson 等 API 的解决方法是使用 TypeReference
并通过创建具有指定参数的实际子 class 来捕获所有类型参数,例如
new TypeReference<Map<String,Integer>>() {}
当您将其传递给 create
方法时,编译器将能够推断其泛型类型参数。反射甚至允许您在运行时检索参数化——类型擦除并不像经常出现的那样普遍。
假设我们有一个工厂,它通过 class 创建对象。问题是 - 如何为通用 classes 声明这样的工厂?例如,可以创建 Map
如果不可能,我想从语言设计的角度知道为什么。
编辑:正如 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
}
}
这是不可能的 - 原因是泛型是通过擦除而不是具体化实现的。这个的简短版本是"generics don't exist at runtime";它们本质上仅用于编译期间的类型检查。所以没有 Map<String, Integer>.class
或 Map<Long, List<String>>.class
这样的东西 - 只有对象 Map.class
.
但从好的方面来说,一个对象也只能真正是class Map
,因为擦除。因此,如果你通过反射构造一个原始类型 Map.class
的对象,并将其转换为 Map<String, Integer>
,那么它 是 一个 Map<String, Integer>
与所有与以正常方式创建的属性相同。
警告是正确的,因为它是说您正在断言编译器无法自动检查的通用边界。由于上述原因,这是不可避免的,在本例中不是问题。我会在这里添加评论,取消警告并继续。我不知道有什么 better/cleaner 方法可以实现您在这里所做的事情。
一个class文字表达式只表示原始类型。你不能 post-hoc 参数化它的引用对象,因为它是一个类型已经固定的常量。因此,您无法传递类型参数信息,您需要让编译器推断完全参数化的泛型类型。
适用于 Jackson 等 API 的解决方法是使用 TypeReference
并通过创建具有指定参数的实际子 class 来捕获所有类型参数,例如
new TypeReference<Map<String,Integer>>() {}
当您将其传递给 create
方法时,编译器将能够推断其泛型类型参数。反射甚至允许您在运行时检索参数化——类型擦除并不像经常出现的那样普遍。