为什么Java允许类型不安全的arrays分配?

通常,Java可以被视为类型安全的语言。 我知道generics有一些缺陷,但我最近遇到了一个前所未有的问题。 要打破它:

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

不会导致预期的编译时错误。 我假设Object数组的声明将不允许指向其他数组。 在generics中,我不允许做出如下奇怪的事情:

 ArrayList objs = new ArrayList 

如果我试图欺骗Java做一些事情

 ArrayList objects = new ArrayList 

我被允许声明它,但我只能添加null类型的对象。

为什么Java不能阻止声明这样的数据呢?

除了“传统设计”之外,我认为除此之外没有答案。 (我承认这是一种说“因为”的奇特方式。)你几乎需要能够以某种方式完成你所展示的最后一个作业。 (否则你会坚持使用手动上/下演员制作大量的副本,假设Java 1.4的语言特性)

在Java 1中,当数组的类型语义基本上是一成不变的时候,generics不可用,或者甚至还需要考虑很长时间。 所以没有可用的机制来表达使这个构造类型安全所需的高阶类型约束 – 而Gosling(IIRC是一个简单的粉丝)觉得解决这个编译时类型安全的边缘情况并不值得复杂的语言无论有哪种解决方案。 或者没有在运行时进行足够的检查以寻找解决方案。 (在一天结束时,语言设计决策至少在某种程度上是任意的,并且只有一个人可以肯定地回答这个问题。)

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

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

因为会抛出exception。 (它不是静态类型安全的……但这完全是另一种说法。)

Java允许这一点的原因是历史性的。 在Java 5之前,Java不支持任何forms的generics。 Gosling说如果他们有时间弄清楚并将generics纳入Java 1.0,他们就会这样做。

不幸的是,他们没有。 但他们仍然希望能够使用以下签名编写类似通用排序方法的内容:

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

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

“因为它必须”。

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

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

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

实际上在数组的情况下,当你添加错误类型的元素时,你在运行时得到一个名为ArrayStoreException的exception在这种情况下是一个String 。 在generics的情况下,没有这样的例外 。 出于very same reason你不能添加任何东西而不是对象,因为你可能只是在列表中添加了错误的类型。

有我在谷歌时发现的讨论

我发现:

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

我认为混乱发生在这里,因为一方面,因为一个String是一个对象,一个字符串数组显然是一个对象数组,而另一方面它显然不是。 答案是数组的可变性。

如果数组是不可变的,那么将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)