这是许多个月前 Java 开发人员讨价还价的结果。虽然看起来很奇怪,但此功能对于许多方法都很重要,例如 Arrays.sort(它也恰好在 Collections.sort 中调用)。基本上,如果 X[](其中 X 是 Object 的某个子类)不被视为子类型,则任何将 Object[] 作为参数的方法都将停止按预期执行。例如,数组可能已被重新设计,以至于在某些情况下它们是只读的,但是问题变成了“什么时候?”。
一方面,将作为参数传递给方法的数组设为只读可能会阻碍编码人员进行原位修改的能力。另一方面,当数组作为参数传递时例外,仍然允许编码器进行非法修改,例如当调用者传递一个整数数组时存储一个字符串。
但是说“Integer[](例如)不是 Object[] 的子类型”的结果是一个危机,其中必须为 Object[] 和 Integer[] 创建一个单独的方法。通过扩展这种逻辑,我们可以进一步说必须为 String[]、Comparable[] 等创建单独的方法。每种类型的数组都需要单独的方法,即使这些方法在其他方面完全相同 em>。
这正是我们拥有多态性的那种情况。
不幸的是,这里允许多态性确实允许尝试在数组中非法存储一个值,如果发生这种情况,则会抛出ArrayStoreException。然而,这是一个很小的代价,而且与ArrayIndexOutOfBoundsException 一样可以避免。
ArrayStoreException 在大多数情况下可以通过两种方式轻松预防(尽管您无法控制其他人的行为)。
1)
不要在不知道它的实际组件类型的情况下尝试将对象存储在数组中。当你正在使用的数组被传入方法时,你不一定知道它来自哪里,所以你不能假设它是安全的,除非组件类型的类是最终的(即没有子类)。
如果数组是从方法返回的,就像上面的问题一样,请了解方法。实际类型是否可能是返回类型的子类?如果是这样,您必须考虑到这一点。
2)
首次初始化在本地使用的数组时,请使用 X[] blah = new X[...]; 或 X[] blah = {...}; 或(从 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" 因此在决定采用哪条路径时,应考虑将如何影响您的其余代码。
我只想以关于泛型的说明结束(如之前的回答中所述)。泛型实际上同样能够让毫无戒心的编码人员感到惊讶。考虑以下代码 sn-p,(修改自 here)。
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