为什么Java中的泛型只能与类一起工作,而不能与基本类型一起工作?

例如,这很好:

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

但这是不允许的:

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

当前回答

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

其他回答

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中,泛型的工作方式是…至少在某种程度上……因为它们是在语言被设计出来数年后才被添加到语言中的。语言设计者在泛型的选择上受到了限制,因为他们必须提出一种向后兼容现有语言和Java类库的设计。

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


1 - Java 1.0中没有包含泛型的原因是时间压力。他们觉得必须尽快发布Java语言,以填补web浏览器带来的新市场机会。詹姆斯·高斯林(James Gosling)曾表示,如果有时间的话,他会想要加入仿制药。如果发生这种情况,Java语言将会是什么样子,这是任何人的猜测。

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

在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”,所以我们不能使用它 作为泛型类型。

供你参考:瓦尔哈拉计划正试图解决上述问题。

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

这应该会在Valhalla项目下的Java 10中出现。

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

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

Java's current erased implementation which produces one class for all reference instantiations and no support for primitive instantiations. (This is a homogeneous translation, and the restriction that Java's generics can only range over reference types comes from the limitations of homogeneous translation with respect to the bytecode set of the JVM, which uses different bytecodes for operations on reference types vs primitive types.) However, erased generics in Java provide both behavioral parametricity (generic methods) and data parametricity (raw and wildcard instantiations of generic types.)

...

a homogeneous translation strategy was chosen, where generic type variables are erased to their bounds as they are incorporated into bytecode. This means that whether a class is generic or not, it still compiles to a single class, with the same name, and whose member signatures are the same. Type safety is verified at compile time, and runtime is unfettered by the generic type system. In turn, this imposed the restriction that generics could only work over reference types, since Object is the most general type available, and it does not extend to primitive types.