通用 类 和 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>.classMap<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 方法时,编译器将能够推断其泛型类型参数。反射甚至允许您在运行时检索参数化——类型擦除并不像经常出现的那样普遍。