为什么我尝试将Integer
添加到声明为Object[]
但实例化为String[]
的Java数组中,而不会生成编译错误?
Object[] ob = new String[1];
ob[0] = new Integer(1); // this shouldn’t compile but it does!
当我运行它时,我得到一个运行时异常而不是(更喜欢的)编译时错误!这是正确的行为吗?我不应该得到编译时错误吗?
答案 0 :(得分:3)
这是Java设计人员做出的选择:String[]
扩展Object[]
,但除了String
之外,您无法向具体 type为String[]
而没有获得运行时异常。
他们可能使之无效的内容如下:
Object[] ob = new String[1];
因为它有效地允许将任何类型的对象添加到数组而不会出现任何编译器错误(如您所注意到的),这完全正常,因为Integer 是一个Object,而编译时类型的数组是Object[]
。
他们没有为数组做出这种选择,但是他们为通用集合做了这个选择:
List<Object> ob = new ArrayList<String>();
生成编译器错误,因为List<String>
未扩展List<Object>
。集合通常应优先于数组,因为它们更安全,并提供更多功能。
答案 1 :(得分:2)
您可能需要考虑的要点是引用数组(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的协变/逆变位置规则。