Answers:
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
),而原始类型则不是。因此它们不能用于泛型。
在Java中,泛型的工作方式与过去一样……至少部分……是因为在设计语言多年后,将它们添加到了语言中 1。语言设计人员不得不提出一种与现有语言和Java类库向后兼容的设计,从而限制了他们对泛型的选择。。
其他编程语言(例如C ++,C#,Ada)确实允许将原始类型用作泛型的参数类型。但是这样做的另一面是,此类语言对泛型(或模板类型)的实现通常需要为每种类型参数化生成泛型的不同副本。
1-Java 1.0中未包含泛型的原因是时间紧迫。他们感到必须尽快发布Java语言,以填补网络浏览器带来的新市场机会。詹姆斯·高斯林(James Gosling)表示,如果他们有时间,他希望将仿制药包括在内。如果发生这种情况,Java语言的外观将是所有人的猜测。
在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项目正试图解决上述问题。
集合被定义为需要从派生的类型java.lang.Object
。基本类型根本不这样做。
根据Java文档,通用类型变量只能使用引用类型实例化,而不能使用原始类型实例化。
它应该在Valhalla项目的 Java 10中出现。
在Brian Goetz关于专业化状态的论文中
关于原始不支持泛型的原因,有很好的解释。并且,它将如何在Java的未来版本中实现。
Java当前的擦除实现为所有引用实例生成一个类,并且不支持原始实例。(这是同质转换,Java泛型只能在引用类型上起作用的限制来自相对于JVM字节码集的同质转换的限制,JVM对字节码集使用不同的字节码进行引用类型和原始类型的操作。)但是,Java中已擦除的泛型提供了行为参数(泛型方法)和数据参数(泛型类型的原始和通配符实例化)。
...
选择了一种同质转换策略,其中通用类型变量在合并到字节码中时被擦除到其边界。这意味着,无论一个类是否通用,它仍将编译为具有相同名称且成员签名相同的单个类。类型安全性在编译时进行了验证,并且运行时不受泛型类型系统的束缚。反过来,这施加了一个限制,即泛型只能在引用类型上使用,因为Object是可用的最通用的类型,并且不扩展到基本类型。