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

Why don't Java Generics support primitive types?

为什么Java中的泛型与类一起工作,而不是用基元类型?

例如,这很好:

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

但这是不允许的:

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


Java中的泛型是一个完整的编译时构造——编译器将所有通用的应用程序转换为正确的类型。这是为了保持与以前的JVM运行时的向后兼容性。

这是:

1
2
3
List<ClassA> list = new ArrayList<ClassA>();
list.add(new ClassA());
ClassA a = list.get(0);

变成(大致上):

1
2
3
List list = new ArrayList();
list.add(new ClassA());
ClassA a = (ClassA)list.get(0);

因此,任何用作泛型的东西都必须可以转换为对象(在本例中,get(0)返回Object,而基元类型则不是,因此它们不能用于泛型。


在爪哇,仿制药的工作方式是…至少部分…因为它们是在语言设计多年后添加到语言中的。语言设计者在对泛型的选择中受到限制,必须想出一种与现有语言和Java类库向后兼容的设计。

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

1——泛型不包含在Java 1中的原因是因为时间压力。他们觉得必须迅速发布Java语言来填补Web浏览器提出的新的市场机会。詹姆斯戈斯林已经声明,如果他们有时间的话,他会愿意包括仿制药。如果发生这种情况,Java语言会是什么样子,这是任何人的猜测。


在Java泛型中使用"类型擦除"来实现向后兼容性。所有泛型类型都在运行时转换为对象。例如,

1
2
3
4
5
6
7
8
public class Container<T> {

    private T data;

    public T getData() {
        return data;
    }
}

将在运行时被视为,

1
2
3
4
5
6
7
8
public class Container {

    private Object data;

    public Object getData() {
        return data;
    }
}

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

1
2
Container<Integer> val = new Container<Integer>();
Integer data = val.getData()

将成为

1
2
Container val = new Container();
Integer data = (Integer) val.getData()

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

Answer is Object is superclass of all objects and can represent any
user defined object.

Since all primitives doesn't inherit from"Object" so we can't use it
as a generic type.

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


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


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

这应该是在瓦尔哈拉项目下的Java 10中实现的。

在布莱恩·戈茨关于专业化状态的论文中

对于原语不支持泛型的原因,有一个很好的解释。而且,它将如何在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.