为什么Java Generics不支持原始类型?

sgo*_*les 222 java generics primitive

为什么Java中的泛型使用类而不是基本类型?

例如,这工作正常:

List<Integer> foo = new ArrayList<Integer>();
Run Code Online (Sandbox Code Playgroud)

但这是不允许的:

List<int> bar = new ArrayList<int>();
Run Code Online (Sandbox Code Playgroud)

the*_*oop 229

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

这个:

List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);
Run Code Online (Sandbox Code Playgroud)

变成(大致):

List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);
Run Code Online (Sandbox Code Playgroud)

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

  • 为什么Java编译器在使用之前也不能将原始类型包装起来?这可能是对的吗? (19认同)
  • @vrwim - 这可能是可能的.但它只是语法糖.真正的问题是,与使用实际基元类型的C++/C#模型相比,具有盒装基元的Java泛型在时间和空间上都相对昂贵. (11认同)
  • @DanyalAytekin - 实际上,Java泛型根本不像C++模板那样处理...... (8认同)
  • @MauganRa是的我知道我可以:)我坚持认为这是一个糟糕的设计.希望它能在java 10(或者我听说过)以及更高阶函数中得到修复.不要引用我的话. (6认同)
  • @Ced完全同意这是一个糟糕的设计,无休止地伤害初学者和专业人士 (4认同)
  • @danyal:不 - 相同的类实现用于所有通用用途.所有这些都是在正确的地方使用直接演员来实现的. (2认同)
  • 对于那些坚持认为这是“糟糕的设计”的人来说,答案是它**必须是这样**。另一种选择是对 Java 语言进行重大更改,这将要求 Sun/Oracle 的现有(付费)客户审查数十亿行源代码。这是不可接受的。(那是 2004 年的事。2018 年的代码审查量将增加 2 到 3 个数量级。) (2认同)

Ste*_*n C 35

在Java中,泛型以他们的方式工作......至少部分......因为在语言设计之后的几年内它们被添加到语言中1.语言设计者通过不得不提出一种向后兼容现有语言和Java类库的设计来限制他们对泛型的选择.

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


1 - Java 1.0中未包含泛型的原因是由于时间压力.他们认为他们必须快速发布Java语言以填补Web浏览器提供的新市场机会.詹姆斯·高斯林曾表示,如果他们有时间,他会喜欢包括仿制药.如果发生这种情况,Java语言会是什么样的,这是任何人的猜测.


vin*_*inS 9

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

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

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

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

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

...

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


Piy*_*gar 7

在java中,通过使用"类型擦除"来实现向后兼容性.所有泛型类型都在运行时转换为Object.例如,

public class Container<T> {

    private T data;

    public T getData() {
        return data;
    }
}
Run Code Online (Sandbox Code Playgroud)

将在运行时看到,

public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Container<Integer> val = new Container<Integer>();
Integer data = val.getData()
Run Code Online (Sandbox Code Playgroud)

会变成

Container val = new Container();
Integer data = (Integer) val.getData()
Run Code Online (Sandbox Code Playgroud)

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

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

由于所有原语都不从" Object " 继承,因此我们不能将它用作泛型类型.

仅供参考:Valhalla项目正试图解决上述问题.


Zei*_*ssS 6

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

  • 我认为这里的问题是"为什么".为什么泛型需要对象?一致认为,它不是一种设计选择,而是更倾向于向后兼容.在我看来,如果泛型不能处理原语,那就是功能缺陷.就目前而言,必须为每个原语编写涉及原语的所有内容:而不是Comparator <t,t>,我们有Integer.compare(int a,int b),Byte.compare(byte a,byte b)等.那不是解决方案! (25认同)