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

时间:2012-12-03 14:20:46

标签: java arrays runtime-error declaration type-safety

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

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

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

ArrayList<Object> objs = new ArrayList<Integer>

如果我试图通过

来欺骗Java
ArrayList<? extends Object> objects = new ArrayList<Integer>

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

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

5 个答案:

答案 0 :(得分:12)

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

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

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

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

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

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

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

答案 1 :(得分:7)

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

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

答案 2 :(得分:4)

“因为必须”。

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

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

我在谷歌搜索时找到了Discussion

我找到了:

  

首先,数组不会破坏类型安全性。如果他们这样做,那么向上转播   数组在运行时不会失败。他们让它变得不可能   编译器证明程序类型安全,因此检查是延迟的   直到运行时。

     

我认为混乱发生在这里,因为一方面,因为一个字符串   is-a Object一个字符串数组显然是一个对象数组,和   另一方面显然不是。答案是一个可变性   阵列。

     

如果数组是不可变的,那么将String []视为一个安全的方法是安全的   Object [],因为不可变的String []总是完全像一个   immutable 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)