为什么 Java 泛型不支持基元类型?

Why don't Java Generics support primitive types?

提问人:Saurabh Gokhale 提问时间:4/27/2010 最后编辑:MichaelSaurabh Gokhale 更新时间:6/4/2020 访问量:103731

问:

为什么 Java 中的泛型可以处理类,而不能处理基元类型?

例如,这工作正常:

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

但这是不允许的:

List<int> bar = new ArrayList<int>();
Java 泛型原

评论

1赞 Sachin Verma 2/26/2018
int i=(int)新对象();不过编译得很好。

答:

294赞 thecoop 4/27/2010 #1

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

评论

11赞 Stephen C 3/26/2012
@DanyalAytekin - 事实上,Java 泛型根本不像C++模板那样处理......
29赞 vrwim 1/3/2014
为什么 Java 编译器不能在使用原始类型之前对其进行装箱?这应该是可能的,对吧?
19赞 Stephen C 6/23/2015
@vrwim - 有可能。但这只是句法糖。真正的问题是,与C++ / C#模型相比,带有盒装原语的Java泛型在时间和空间上都相对昂贵......其中使用实际的基元类型。
7赞 Ced 2/24/2017
@MauganRa是的,我知道我可以:)我坚持自己的立场,这是可怕的设计。希望它能在 java 10(我听说)和更高阶函数中得到修复。不要引用我的话。
4赞 MauganRa 2/26/2017
@Ced完全同意,这是糟糕的设计,它无休止地伤害了初学者和专业人士
51赞 Stephen C 4/27/2010 #2

在 Java 中,泛型的工作方式是......至少部分......因为它们是在语言设计多年后才被添加到语言中的1.语言设计者在泛型选项上受到限制,因为他们必须提出一个与现有语言和 Java 类库向后兼容的设计。

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


1 - 泛型未包含在 Java 1.0 中的原因是时间压力。他们认为他们必须尽快发布 Java 语言,以填补 Web 浏览器带来的新市场机会。詹姆斯·高斯林(James Gosling)表示,如果他们有时间,他希望将仿制药包括在内。如果发生这种情况,Java 语言会是什么样子是任何人的猜测。

7赞 ZeissS 4/27/2010 #3

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

评论

30赞 John P 6/28/2014
我认为这里的问题是“为什么”。为什么泛型需要 Objects?人们的共识似乎是,它与其说是一种设计选择,不如说是向后兼容性。在我看来,如果泛型不能处理原语,那就是功能缺陷。就目前而言,所有涉及基元的内容都必须为每个基元编写:我们有 Integer.compare(int a, int b)、Byte.compare(byte a, byte b) 等,而不是 Comparator<t,t>。这不是一个解决方案!
1赞 crow 12/24/2017
是的,基元类型的泛型将是必不可少的功能。这是该提案的链接 openjdk.java.net/jeps/218
9赞 vinS 12/18/2017 #4

根据 Java 文档,泛型类型变量只能使用引用类型实例化,而不能使用基元类型实例化。

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

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

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

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

...

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

40赞 Piyush Sagar 4/6/2018 #5

在 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 是所有对象的超类,可以表示任何 用户定义的对象。

由于所有基元都不是从“Object”继承的,因此我们不能使用它 作为泛型类型。

仅供参考:瓦尔哈拉项目正试图解决上述问题。

评论

0赞 Drazen Bjelovuk 2/25/2020
加 1 表示正确的命名法。