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

时间:2023-02-06 22:32:47

Generally, Java can be considered as a type-safe language. I know that there are some flaws with generics, but I recently came across a Problem I never had before. To break it down:

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

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

will NOT result in a compile-time error as expected. I would assume that the declaration of an Array of Object will disallow to point to to an array of something else. In Generics I'm not allowed to make such weird things like:

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

ArrayList<Object> objs = new ArrayList<Integer>

and if I try to kind of trick Java into doing something with

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

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

I'm allowed to declare it, but I can only add Objects of type null.

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

Why doesn't Java prevent the declaration of such weired arrays?

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

5 个解决方案

#1


7  

I don't think there's an answer to this besides "legacy design". (Which I admit is a fancy way of saying "because".) You pretty much need to be able to do an equivalent of the last assignment you show somehow. (Otherwise you're stuck to making lots and lots of copies with manual up/down casts, assuming language features of Java pre 1.4)

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

In Java 1 when type semantics for arrays were basically set in stone, generics weren't available, or even up for consideration for a long while yet. So there was no mechanism available to express the higher-order type constraints needed to make this construct type-safe – and Gosling (IIRC a fan of simplicity) felt resolving this edge case of compile-time type safety wasn't worth complicated the language with whichever solutions were available. Or wasn't bothered by doing the check at runtime enough to even look for a solution. (At the end of the day language design decisions are arbitrary to at least some degree, and there's only one person that could answer this with any certainty.)

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

#2


10  

Firstly, I should point out that this is type-safe.

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

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

because an exception will be thrown. (It is not statically type-safe ... but that is a different statement entirely.)

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

The reason that Java allows this is historical. Until Java 5, Java did not support any form of generics. Gosling has said that if they had had the time to figure out and incorporate generics into Java 1.0, they would have done so.

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

Unfortunately, they didn't. But they still wanted to be able write things like a general purpose sort method with the following signature:

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

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

To make this method work for any kind of object array (without generics), it was necessary to make arrays covariant; i.e. to make it legal to pass a String[] or Integer[] as an argument where the formal type is Object[]. If they hadn't done that you would have had to copy the String[] to an Object[], sort it, and then copy it back.

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

#3


3  

"Because it has to".

“因为它必须”。

To elaborate a bit, consider the following example:

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

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

Now, how would the Java compiler know which assignments to allow and which to refuse? It can't. The compile-time type is Object so the compiler will let you put any Object in your array, simply because it doesn't have any knowledge of the runtime type of your array.

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

#4


1  

actually in case of arrays you get a exception at run time called ArrayStoreException when you add wrong type of element In this case a String. In case of generics there is no such exception. its for the very same reason you are not allowed to add anything but object, as you might just add a wrong type into the list.

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

#5


0  

There's Discussion that I found while I google it

有我在谷歌时发现的讨论

I Found:

我发现:

Firstly, arrays do not break type safety. If they did, then upcasting an array wouldn't fail at runtime. They make it impossible for the compiler to prove the program type-safe, so the checking is deferred until runtime.

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

I think confusion occurs here because one the one hand, since a String is-a Object an array of Strings is-obviously-an array of Objects, and on the other it clearly isn't. The answer is in the mutability of an array.

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

If the array is immutable, then it safe to treat a String[] as an Object[], because an immutable String[] is always exactly like an immutable Object[].

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

On the other hand, if the array is mutable, then it is not usually safe to treat a String[] as an Object[].

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

The "wildcards" technique described in the above link is exactly what CommonLisp has been doing for years.

上面链接中描述的“通配符”技术正是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)

#1


7  

I don't think there's an answer to this besides "legacy design". (Which I admit is a fancy way of saying "because".) You pretty much need to be able to do an equivalent of the last assignment you show somehow. (Otherwise you're stuck to making lots and lots of copies with manual up/down casts, assuming language features of Java pre 1.4)

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

In Java 1 when type semantics for arrays were basically set in stone, generics weren't available, or even up for consideration for a long while yet. So there was no mechanism available to express the higher-order type constraints needed to make this construct type-safe – and Gosling (IIRC a fan of simplicity) felt resolving this edge case of compile-time type safety wasn't worth complicated the language with whichever solutions were available. Or wasn't bothered by doing the check at runtime enough to even look for a solution. (At the end of the day language design decisions are arbitrary to at least some degree, and there's only one person that could answer this with any certainty.)

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

#2


10  

Firstly, I should point out that this is type-safe.

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

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

because an exception will be thrown. (It is not statically type-safe ... but that is a different statement entirely.)

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

The reason that Java allows this is historical. Until Java 5, Java did not support any form of generics. Gosling has said that if they had had the time to figure out and incorporate generics into Java 1.0, they would have done so.

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

Unfortunately, they didn't. But they still wanted to be able write things like a general purpose sort method with the following signature:

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

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

To make this method work for any kind of object array (without generics), it was necessary to make arrays covariant; i.e. to make it legal to pass a String[] or Integer[] as an argument where the formal type is Object[]. If they hadn't done that you would have had to copy the String[] to an Object[], sort it, and then copy it back.

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

#3


3  

"Because it has to".

“因为它必须”。

To elaborate a bit, consider the following example:

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

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

Now, how would the Java compiler know which assignments to allow and which to refuse? It can't. The compile-time type is Object so the compiler will let you put any Object in your array, simply because it doesn't have any knowledge of the runtime type of your array.

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

#4


1  

actually in case of arrays you get a exception at run time called ArrayStoreException when you add wrong type of element In this case a String. In case of generics there is no such exception. its for the very same reason you are not allowed to add anything but object, as you might just add a wrong type into the list.

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

#5


0  

There's Discussion that I found while I google it

有我在谷歌时发现的讨论

I Found:

我发现:

Firstly, arrays do not break type safety. If they did, then upcasting an array wouldn't fail at runtime. They make it impossible for the compiler to prove the program type-safe, so the checking is deferred until runtime.

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

I think confusion occurs here because one the one hand, since a String is-a Object an array of Strings is-obviously-an array of Objects, and on the other it clearly isn't. The answer is in the mutability of an array.

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

If the array is immutable, then it safe to treat a String[] as an Object[], because an immutable String[] is always exactly like an immutable Object[].

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

On the other hand, if the array is mutable, then it is not usually safe to treat a String[] as an Object[].

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

The "wildcards" technique described in the above link is exactly what CommonLisp has been doing for years.

上面链接中描述的“通配符”技术正是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)