265

为什么 Java 中的泛型适用于类而不适用于原始类型?

例如,这很好用:

List<Integer> foo = new ArrayList<Integer>();

但这是不允许的:

List<int> bar = new ArrayList<int>();
4

5 回答 5

272

Java 中的泛型是一个完全编译时的构造——编译器将所有泛型使用转换为正确类型的强制转换。这是为了保持与以前的 JVM 运行时的向后兼容性。

这:

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

变成(大致):

List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);

因此,任何用作泛型的东西都必须可转换为 Object(在此示例中get(0)返回一个Object),而原始类型则不是。所以它们不能用于泛型。

于 2010-04-27T13:26:02.030 回答
46

在 Java 中,泛型以它们的方式工作......至少部分......因为它们是在语言设计多年后被添加到语言中的1由于不得不提出一种与现有语言和 Java 类库向后兼容的设计,语言设计者对泛型的选择受到了限制

其他编程语言(例如 C++、C#、Ada)确实允许将原始类型用作泛型的参数类型。但这样做的另一面是,此类语言的泛型(或模板类型)实现通常需要为每个类型参数化生成泛型类型的不同副本。


1 - Java 1.0 中没有包含泛型的原因是时间压力。他们认为必须尽快发布 Java 语言才能填补网络浏览器带来的新市场机会。James Gosling 曾表示,如果他们有时间,他会希望包括仿制药。如果发生这种情况,Java 语言会是什么样子,谁也说不准。

于 2010-04-27T13:31:53.730 回答
26

在 Java 中,泛型是通过使用“类型擦除”来实现的,以实现向后兼容性。所有泛型类型都在运行时转换为 Object。例如,

public class Container<T> {

    private T data;

    public T getData() {
        return data;
    }
}

将在运行时被视为,

public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}

编译器负责提供适当的强制转换以确保类型安全。

Container<Integer> val = new Container<Integer>();
Integer data = val.getData()

会变成

Container val = new Container();
Integer data = (Integer) val.getData()

现在的问题是为什么在运行时选择“对象”作为类型?

答案是Object是所有对象的超类,可以表示任何用户定义的对象。

由于所有原语都不是从“ Object ”继承的,所以我们不能将它用作泛型类型。

仅供参考:Valhalla 项目正在尝试解决上述问题。

于 2018-04-06T06:26:12.553 回答
9

根据Java 文档,泛型类型变量只能用引用类型实例化,不能用原始类型实例化。

这应该出现在Project Valhalla下的 Java 10 中。

在关于专业化状态的Brian Goetz论文中

关于原语不支持泛型的原因有一个很好的解释。并且,它将如何在 Java 的未来版本中实现。

Java 当前已擦除的实现,它为所有引用实例化生成一个类,并且不支持原始实例化。(这是一种同构翻译,Java 的泛型只能覆盖引用类型的限制来自同构翻译相对于 JVM 字节码集的限制,JVM 对引用类型和原始类型的操作使用不同的字节码。)但是,Java 中的擦除泛型提供了行为参数(泛型方法)和数据参数(泛型的原始和通配符实例化)。

...

选择了同质翻译策略,其中泛型类型变量在合并到字节码中时被擦除到它们的边界。这意味着无论一个类是否是泛型的,它仍然编译为一个类,具有相同的名称,并且其成员签名相同。类型安全在编译时验证,运行时不受泛型类型系统的限制。反过来,这强加了泛型只能在引用类型上工作的限制,因为 Object 是最通用的可用类型,并且它不会扩展到原始类型。

于 2017-12-18T11:45:22.767 回答
7

集合被定义为需要一个派生自java.lang.Object. 基本类型根本不这样做。

于 2010-04-27T13:41:18.067 回答