为什么我尝试将 an 添加Integer
到 Java 数组(声明为 anObject[]
但实例化为 a String[]
)不会产生编译错误?
Object[] ob = new String[1];
ob[0] = new Integer(1); // this shouldn’t compile but it does!
当我运行它时,我得到一个运行时异常,而不是一个(非常喜欢的)编译时错误!这是正确的行为吗?我不应该得到一个编译时错误吗?
为什么我尝试将 an 添加Integer
到 Java 数组(声明为 anObject[]
但实例化为 a String[]
)不会产生编译错误?
Object[] ob = new String[1];
ob[0] = new Integer(1); // this shouldn’t compile but it does!
当我运行它时,我得到一个运行时异常,而不是一个(非常喜欢的)编译时错误!这是正确的行为吗?我不应该得到一个编译时错误吗?
这是 Java 设计人员做出的选择:String[]
extends Object[]
,但是除了 a 之外,您不能将任何其他内容添加到具体类型为没有运行时异常String
的数组中。String[]
他们可能使无效的是以下内容:
Object[] ob = new String[1];
因为它有效地允许将任何类型的对象添加到数组中而不会出现任何编译器错误(如您所见),这是完全正常的,因为 Integer是Object,并且数组的编译时类型是Object[]
.
他们没有为数组做出这个选择,但他们为泛型集合做了这个选择:
List<Object> ob = new ArrayList<String>();
生成编译器错误,因为List<String>
不扩展List<Object>
. 集合通常应该优先于数组,因为它们更安全,并且提供更多功能。
您可能需要考虑的要点是引用数组的运行时类型 ( String[]
) 与数组变量的编译时类型 ( Object[]
)。编译器仅强制您放入数组的内容与引用它的数组变量的静态类型兼容。这个例子使它脱颖而出:
String[] strs = new String[1];
Object[] objs = strs;
strs[0] = new Integer(1); /* error, says the compiler! */
objs[0] = new Integer(1); /* fine by me, says the compiler! */
现在,你理所当然地应该对你的第一行感到不安:Object[] ob = new String[1];
这意味着它String[]
与赋值兼容Object[]
——换句话说,它的子类型。这与 CS 中众所周知的事实相反,即容器类型在其元素的类型上是逆变的。为什么Java不是这样?我曾经读过一个帐户,其中有人问 James Gosling,他回答说他们知道事实,但想要一种简单的方法让 Java 方法能够接受任何类型的数组(通过Object[]
类型)。这源于对 Java 成为“蓝领语言”的要求,而不是陷入类型理论的精确性,这会带来自己的心理包袱。见证泛型<? super String>
和 Scala 的协变/逆变位置规则。