【问题标题】:Why don't Java Generics support primitive types?为什么 Java 泛型不支持原始类型?
【发布时间】:2021-07-06 10:54:01
【问题描述】:

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

例如,这很好用:

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

但这是不允许的:

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

【问题讨论】:

  • int i=(int)new Object();编译就好了。

标签: java generics primitive


【解决方案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),而原始类型则不是。所以它们不能用于泛型。

【讨论】:

  • @DanyalAytekin - 事实上,Java 泛型根本不像 C++ 模板那样处理......
  • 为什么 Java 编译器不能在原始类型被使用之前将其装箱?这应该是可能的吧?
  • @vrwim - 有可能。但这只是语法糖。真正的问题是,与使用实际基元类型的 C++/C# 模型相比,具有盒装基元的 Java 泛型在时间和空间上都相对昂贵。
  • @MauganRa 是的,我知道我可以:) 我坚持认为这是一个糟糕的设计。希望它会在 java 10 中得到修复(或者我听说过)以及更高阶的函数。不要引用我的话。
  • @Ced 完全同意这种糟糕的设计会无休止地伤害初学者和专业人士
【解决方案2】:

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

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


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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 正确命名加 1。
    【解决方案4】:

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

    这应该在Project Valhalla 下的 Java 10 中提供。

    在Brian GoetzState of the Specialization 上的论文中

    有一个excellent explanation 关于原语不支持泛型的原因。并且,how it will be implemented 在 Java 的未来版本中。

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

    ...

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 我认为这里的问题是“为什么”。为什么泛型需要对象?共识似乎是,它不是一种设计选择,而是更多地向向后兼容性屈服。在我看来,如果泛型不能处理原语,那就是功能缺陷。就目前而言,必须为每个基元编写涉及基元的所有内容:我们使用 Integer.compare(int a, int b)、Byte.compare(byte a, byte b) 等代替 Comparator。这不是解决方案!
      • 是的,泛型优于原始类型将是必备功能。这是一个提案的链接openjdk.java.net/jeps/218
      猜你喜欢
      • 2011-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-12
      相关资源
      最近更新 更多