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


236

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

例如,这可以正常工作:

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

但这是不允许的:

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

1
int i =(int)new Object(); 编译虽然很好。
Sachin Verma '18

Answers:


243

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),而原始类型则不是。因此它们不能用于泛型。


8
@DanyalAytekin-实际上,Java泛型根本不像C ++模板那样处理……
Stephen C

20
为什么Java编译器不能在使用原始类型之前也将其装箱?这应该可能吧?
vrwim 2014年

13
@vrwim-有可能。但这只是语法糖。真正的问题是带有盒装原语的Java泛型与使用实际原语类型的C ++ / C#模型相比,在时间和空间上都相对昂贵。
2015年

6
@MauganRa是的,我知道我可以:)我坚持自己的观点,这是糟糕的设计。希望它将在Java 10(或我听说的)以及更高阶的函数中得到修复。不要在那上面引用我。
Ced

4
@Ced完全同意它的不良设计无休止地伤害初学者和专业人士
MauganRa

37

在Java中,泛型的工作方式与过去一样……至少部分……是因为在设计语言多年后,将它们添加到了语言中 1。语言设计人员不得不提出一种与现有语言和Java类库向后兼容的设计,从而限制了他们对泛型的选择。

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


1-Java 1.0中未包含泛型的原因是时间紧迫。他们感到必须尽快发布Java语言,以填补网络浏览器带来的新市场机会。詹姆斯·高斯林(James Gosling)表示,如果他们有时间,他希望将仿制药包括在内。如果发生这种情况,Java语言的外观将是所有人的猜测。


11

在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项目正试图解决上述问题。


加1可以正确命名。
Drazen Bjelovuk

7

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


26
我认为这里的问题是“为什么”。为什么泛型需要对象?共识似乎是,它不是设计选择,而是更多地向后兼容。在我看来,如果泛型无法处理原语,那就是功能上的不足。就目前而言,必须为每个图元编写涉及图元的所有内容:我们有Integer.compare(int a,int b),Byte.compare(byte a,byte b)等来代替Comparator <t,t>。那不是解决方案!
约翰·P

1
是的,对原始类型的泛型将是必须具备的功能。这是对它的建议的链接openjdk.java.net/jeps/218
乌鸦

5

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

它应该在Valhalla项目的 Java 10中出现。

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

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

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

...

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


0

创建对象时,不能用原始类型代替type参数。关于此限制的原因,这是编译器实现的问题。基本类型具有自己的字节码指令,用于加载和存储到虚拟机堆栈中。因此,将原始泛型编译到这些单独的字节码路径中并非没有可能,但这会使编译器变得复杂。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.