16

一般来说,Java 可以被认为是一种类型安全的语言。我知道泛型存在一些缺陷,但我最近遇到了一个以前从未遇到过的问题。分解它:

Object[] objects = new Integer[10];
objects[0] = "Hello World";

不会像预期的那样导致编译时错误。我会假设 Array of 的声明Object将不允许指向其他东西的数组。在泛型中,我不允许做出如此奇怪的事情,例如:

ArrayList<Object> objs = new ArrayList<Integer>

如果我试图欺骗 Java 做某事

ArrayList<? extends Object> objects = new ArrayList<Integer>

我可以声明它,但我只能添加类型的对象null

为什么 Java 不阻止声明这种怪异的数组?

4

5 回答 5

13

首先,我应该指出这是类型安全的。

Object[] objects = new Integer[10];
objects[0] = "Hello World";

因为会抛出异常。(它不是静态类型安全的......但这完全是一个不同的陈述。)

Java 允许这样做的原因是历史性的。在 Java 5 之前,Java 不支持任何形式的泛型。Gosling 曾说过,如果他们有时间弄清楚泛型并将其合并到 Java 1.0 中,他们就会这样做。

不幸的是,他们没有。但是他们仍然希望能够编写具有以下签名的通用排序方法之类的东西:

    void sort(Object[] array, Comparator comp) ...

为了使这个方法适用于任何类型的对象数组(没有泛型),有必要使数组协变;即,使传递 a String[]orInteger[]作为形式类型为 的参数合法Object[]。如果他们没有这样做,您将不得不将 复制String[]Object[],对其进行排序,然后将其复制回来。

于 2012-12-03T14:50:58.747 回答
7

我认为除了“遗留设计”之外没有其他答案。(我承认这是说“因为”的一种奇特方式。)您几乎需要能够以某种方式完成与您展示的最后一项任务相同的任务。(否则,假设 Java 1.4 之前的语言特性,您将不得不使用手动向上/向下转换来制作大量副本)

在 Java 1 中,当数组的类型语义基本上是一成不变的时,泛型不可用,甚至在很长一段时间内都没有考虑到泛型。所以没有可用的机制来表达使这个构造类型安全所需的高阶类型约束——Gosling(IIRC 是简单的粉丝)认为解决编译时类型安全的这种边缘情况并不值得复杂语言使用任何可用的解决方案。或者没有在运行时进行检查而烦恼,甚至没有寻找解决方案。(归根结底,语言设计决策至少在某种程度上是任意的,只有一个人可以肯定地回答这个问题。)

于 2012-12-03T14:34:51.527 回答
4

“因为它必须”。

为了详细说明,请考虑以下示例:

Object[] objects = null;
if (something) {
    objects = new Integer[10];
} else {
    objects = new String[10];
}

现在,Java 编译器如何知道允许哪些分配以及拒绝哪些分配?它不能。编译时类型是 Object,因此编译器允许您将任何 Object 放入数组中,这仅仅是因为它不知道数组的运行时类型。

于 2012-12-03T14:29:37.207 回答
1

实际上,在数组的情况下,当您添加错误类型的元素时,您会在运行时遇到一个名为ArrayStoreException的异常,在这种情况下为String。在泛型的情况下没有这样的例外。它是因为very same reason您不允许添加除对象之外的任何内容,因为您可能只是在列表中添加了错误的类型。

于 2012-12-03T14:29:01.620 回答
0

我在谷歌搜索时发现了一个讨论

我发现:

首先,数组不会破坏类型安全。如果他们这样做了,那么在运行时向上转换一个数组就不会失败。它们使编译器无法证明程序类型安全,因此检查被推迟到运行时。

我认为这里发生了混淆,因为一方面,因为 String 是一个 Object 一个 Strings 数组显然是一个 Objects 数组,而另一方面它显然不是。答案在于数组的可变性。

如果数组是不可变的,那么将 String[] 视为 Object[] 是安全的,因为不可变的 String[] 总是与不可变的 Object[] 完全相同。

另一方面,如果数组是可变的,那么将 String[] 视为 Object[] 通常是不安全的。

上述链接中描述的“通配符”技术正是 CommonLisp 多年来一直在做的事情。

(deftype StringArray? () (array String)) ; This is the type of arrays of String 
(deftype ObjectArray? () (array Object)) ; This is the type of arrays of Object 
(subtypep StringArray? ObjectArray?)      ; Is StringArray? a subtype of ObjectArray?? false, true                               ; No, it isn't. (false: it isn't, true: I'm ure) 
(deftype AnyArray? () (array *))         ; This is the type of arrays of anything (subtypep StringArray? AnyArray?)         ; Is StringArray? a subtype of AnyArray??   true, true                                ; Yes, it is. (true: it is, true: I'm sure)
于 2012-12-03T17:01:05.983 回答