【发布时间】:2018-12-11 08:36:02
【问题描述】:
考虑以下示例,忽略人们想要这样做的原因:
private static class Original {
public String getValue() {
return "Foo";
}
}
private static class Wrapper extends Original {
private Original orig;
public Wrapper(Original orig) {
this.orig = orig;
}
@Override
public String getValue() {
return orig.getValue();
}
}
public static void test(Original... o) {
if (o != null && o.length > 0) {
for (int i = 0; i < o.length; i++) {
if (o[i] instanceof Wrapper) {
o[i] = ((Wrapper) o[i]).orig; // Throws java.lang.ArrayStoreException at runtime
}
}
}
}
public static void main(String[] args){
test(new Wrapper[] { // Explicitly create an array of subclass type
new Wrapper(new Original())
});
}
这个例子在编译时没有给出警告或错误。似乎编译器决定 Wrapper[] 包含 Wrapper 实例,这实际上意味着这些绝对是 Original 类的实例。这完全没问题。
但是,在运行时,Wrapper[] 实例直接传递到方法中。我认为在运行时拆除这个数组并重新创建Original[] 的实例会足够聪明,但似乎情况并非如此。
这种行为是否曾经记录在某处(如 JLS)?像我这样的普通程序员总是会假设我可以操纵 Original... 的 vararg 参数,就好像它是 Original[] 一样。
【问题讨论】:
-
那么确切的问题是什么?
-
这个问题是Java泛型不变的原因(例如
List<Dog>不是List<Animal>)。 -
JLS Sec 4.10.3: "如果 S 和 T 都是引用类型,则 S[] >1 T[] iff S >1 T。"
-
我有点困惑
o的类型是Wrapper[]而不是Original[]传递的参数不应该放在可变参数数组中吗?或者与参数类型匹配的数组是否有特殊例外? -
不,@markspace,因为
Wrapper的数组不是Original,你不能将它放在Original的数组中。从其他引用类型你可能知道对象的运行时类型可以比引用变量的声明类型更精确。这对于数组也是可能的:o的声明类型是Original的数组,但运行时类型是Wrapper的数组。
标签: java variadic-functions jls