为什么Java允许类型不安全的数组分配?
一般来说,Java可以被视为类型安全语言。我知道泛型有一些缺陷,但我最近遇到了一个以前从未遇到过的问题。 要分解它:为什么Java允许类型不安全的数组分配?,java,arrays,runtime-error,declaration,type-safety,Java,Arrays,Runtime Error,Declaration,Type Safety,一般来说,Java可以被视为类型安全语言。我知道泛型有一些缺陷,但我最近遇到了一个以前从未遇到过的问题。 要分解它: Object[] objects = new Integer[10]; objects[0] = "Hello World"; 不会像预期的那样导致编译时错误。我假设对象数组的声明将不允许指向其他对象数组。在泛型中,我不允许做这样奇怪的事情,比如: ArrayList<Object> objs = new ArrayList<Integer> Arra
Object[] objects = new Integer[10];
objects[0] = "Hello World";
不会像预期的那样导致编译时错误。我假设对象数组的声明将不允许指向其他对象数组。在泛型中,我不允许做这样奇怪的事情,比如:
ArrayList<Object> objs = new ArrayList<Integer>
ArrayList objs=新建ArrayList
如果我试图欺骗Java使用
ArrayList<? extends Object> objects = new ArrayList<Integer>
ArrayList实际上,在数组的情况下,当您添加了错误类型的元素时,在运行时会出现一个名为ArrayStoreException的异常,在这种情况下是字符串。对于泛型,不存在此类异常。这与的原因非常相似
除了对象之外,不允许添加任何内容,因为您可能只是在列表中添加了错误的类型。“因为它必须”
详细说明一下,考虑下面的例子:
Object[] objects = null;
if (something) {
objects = new Integer[10];
} else {
objects = new String[10];
}
现在,Java编译器如何知道允许哪些赋值和拒绝哪些赋值?不可能。编译时类型是Object,因此编译器将允许您在数组中放置任何对象,因为它不知道数组的运行时类型。我认为除了“遗留设计”之外,没有其他答案。(我承认这是说“因为”的一种花哨的方式。)你几乎需要能够以某种方式完成上一个任务。(否则,假设Java pre 1.4的语言特性,您只能通过手动上/下转换制作大量副本)
在Java1中,当数组的类型语义基本上是一成不变的时候,泛型是不可用的,甚至在很长一段时间内都没有考虑泛型。因此,没有可用的机制来表示使这个构造类型安全所需的高阶类型约束——而Gosling(IIRC是一个简单性的粉丝)觉得解决编译时类型安全的边缘问题不值得用任何可用的解决方案来复杂化语言。或者在运行时进行检查时,甚至没有找到解决方案。(归根结底,语言设计决策至少在某种程度上是武断的,只有一个人可以肯定地回答这个问题。)首先,我应该指出,这是类型安全的
Object[] objects = new Integer[10];
objects[0] = "Hello World";
因为将引发异常。(它不是静态类型安全的…但这是一个完全不同的语句。)
Java允许这样做的原因是历史的。在Java5之前,Java不支持任何形式的泛型。高斯林说,如果他们有时间找出泛型并将其合并到Java1.0中,他们就会这样做
不幸的是,他们没有。但他们仍然希望能够编写具有以下签名的通用排序方法:
void sort(Object[] array, Comparator comp) ...
要使此方法适用于任何类型的对象数组(没有泛型),必须使数组协变;i、 e.合法地将字符串[]
或整数[]
作为参数传递,其中形式类型为对象[]
。如果他们没有这样做,您就必须将字符串[]
复制到对象[]
,对其排序,然后将其复制回来。我在谷歌搜索时找到了一个
我发现:
首先,阵列不会破坏类型安全性。如果他们这样做了,那么就向上投射
数组在运行时不会失败。他们让这一切变得不可能
编译器来证明程序类型安全,因此检查被推迟
直到运行时
我想这里会出现混乱,因为一只手,一根绳子
是一个对象字符串数组显然是一个对象数组,并且
另一方面,显然不是。答案是一个变量的易变性
数组
如果数组是不可变的,则可以安全地将字符串[]视为
对象[],因为不可变字符串[]始终与
不可变对象[]
另一方面,如果数组是可变的,那么它通常不是可变的
可以安全地将字符串[]视为对象[]
上面链接中描述的“通配符”技术正是
CommonLisp已经做了很多年了
如果编译器采用与泛型相同的方法,那么在代码示例中,它将不允许对对象
同时赋值。只允许新对象[10]
。-1:“现在,Java编译器如何知道允许哪些赋值和拒绝哪些赋值?”-静态类型检查的整个思想是告诉编译器允许哪些赋值和拒绝哪些赋值。这不是答案,这是对问题的重述。@橡皮擦不,静态类型是“array ofObject
s”-这是与Object
完全不同的类型,即使在Java的类型系统中也是如此。(例如,数组类型重写clone()
,使其成为公共的
,并返回数组本身的类型。)允许整数【】
赋值给对象【】
,同时允许整数
赋值给对象
,这是完全允许的,事实上,这样做是有意义的,因为它不是类型安全的。问题在于所有这些。要获得形式化:Java中的数组是,它允许在编译时无法检测到的类型错误(正如您所指出的)。然而,您的答案(无声地)假设它们必须是协变的——事实并非如此,设计者本可以使它们成为非变量的(正如其他人所指出的,泛型已经是非变量的)。如果它们是非变量的,就可以防止类型错误。这种解释与以下问题中的句子相冲突:“我假设一个对象数组的声明将不允许指向另一个数组。”以及“为什么Java不阻止这样的数组的声明?”(我的重点)我不同意strongl
(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)