这是许多个月前 Java 开发人员讨价还价的结果。虽然看起来很奇怪,但此功能对于许多方法都很重要,例如Arrays.sort
(也恰好在 中调用Collections.sort
)。基本上,如果 X[](其中 X 是 Object 的某个子类)不被视为子类型,则任何将 Object[] 作为参数的方法都将停止按预期执行。例如,数组可能已被重新设计,以至于在某些情况下它们是只读的,但是问题变成了“何时?”。
一方面,将作为参数传递给方法的数组设为只读可能会阻碍编码人员进行原位修改的能力。另一方面,当数组作为参数传递时例外,仍然允许编码器进行非法修改,例如当调用者传递一个整数数组时存储一个字符串。
但是说“Integer[](例如)不是 Object[] 的子类型”的结果是一个危机,其中必须为 Object[] 和 Integer[] 创建一个单独的方法。通过扩展这种逻辑,我们可以进一步说必须为 String[]、Comparable[] 等创建一个单独的方法。每种类型的数组都需要一个单独的方法,即使这些方法在其他方面完全相同。
这正是我们拥有多态性的那种情况。
不幸的是,在这里允许多态性确实允许尝试在数组中非法存储一个值,并且ArrayStoreException
如果发生这样的实例,则会抛出 an 。然而,这是一个很小的代价,并且与ArrayIndexOutOfBoundsException
.
ArrayStoreException
在大多数情况下,可以通过两种方式轻松预防(尽管您无法控制其他人的行为)。
1)不要在不知道 实际组件类型
的情况下尝试将对象存储在数组中。当您正在使用的数组被传递到方法中时,您不一定知道它来自哪里,因此您不能假设它是安全的,除非组件类型的类是最终的(即没有子类)。
如果数组是从方法返回的,就像上面的问题一样,请了解该方法。实际类型是否可能是返回类型的子类?如果是这样,您必须考虑到这一点。
2)
当你第一次初始化一个在本地使用的数组时,使用形式X[] blah = new X[...];
or X[] blah = {...};
or (as of Java 10) var blah = new X[...];
。然后,任何在此数组中存储非 X 值的尝试都将导致编译器错误。你不应该说的是Y[] blah = new X[...];
,其中 X 是 Y 的子类。
如果您有一个数组,就像上面的问题一样,想要存储错误类型的组件,那么就像其他人建议的那样,您必须创建一个正确类型的新数组并将信息复制到...
Object[] o = Arrays.copyOf(s, s.length, Object[].class); //someone demonstrate System.arrayCopy. I figure I show another way to skin cat. :p
o[0] = 42;
或者您必须以某种方式将要存储的组件转换为正确的类型。
s[0] = String.valueOf(42);
请注意, 42 != "42" 因此在决定采用哪条路径时,应考虑将如何影响其余代码。
我只想以关于泛型的说明结束(如之前的答案中所述)。泛型实际上同样能够让毫无戒心的编码人员感到惊讶。考虑以下代码片段(从此处修改)。
import java.util.List;
import java.util.ArrayList;
public class UhOh {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
WildcardFixed.foo(list);
list.add(6);
System.out.println(list); // ¯\_(ツ)_/¯ oh well.
int i = list.get(0); //if we're going to discuss breaches of contract... :p
}
}
class WildcardFixed /*not anymore ;) */ {
static void foo(List<?> i) {
fooHelper(i);
}
private static <T> void fooHelper(List<T> l) {
l.add((T)Double.valueOf(2.5));
}
}
泛型,女士们,先生们。:p