Object[] o = "a;b;c".split(";");
o[0] = 42;抛出
java.lang.ArrayStoreException: java.lang.Integer而
String[] s = "a;b;c".split(";");
Object[] o = new Object[s.length];
for (int i = 0; i < s.length; i++) {
o[i] = s[i];
}
o[0] = 42;不会的。
有没有其他方法可以在不创建临时String[]数组的情况下处理该异常?
发布于 2012-09-11 20:56:51
在Java中,数组也是一个对象。
您可以将子类型的对象放入父类型的变量中。例如,您可以将String对象放入Object变量中。
不幸的是,Java中的数组定义不知何故被破坏了。String[]被认为是Object[]的一个子类型,但这是错误的!有关“协变和逆变”的更详细的解释,请阅读“协变和逆变”,但其本质是:只有当子类型满足超类型的所有义务时,才应将一个类型视为另一个类型的子类型。这意味着,如果你得到一个子类型对象而不是一个超类型对象,你不应该期望行为与父类型协定相矛盾。
问题是String[]只支持Object[]合约的一部分。例如,您可以从Object[]读取Object值。您还可以从String[]读取Object值(恰好是String对象)。到目前一切尚好。问题出在合同的其他部分。您可以将任何Object放入Object[]中。但是您不能将任何Object放入String[]中。因此,String[]不应该被认为是Object[]的一个子类型,但是Java规范说它是一个子类型。因此,我们产生了这样的后果。
(请注意,泛型类也出现了类似的情况,但这一次被正确解决了。List<String>不是List<Object>的子类型;如果您想为这些类型创建一个通用的超类型,则需要List<?>,它是只读的。数组也应该是这样的;但事实并非如此。而且由于向后兼容,现在改变它已经太晚了。)
在第一个示例中,String.split函数创建一个String[]对象。您可以将其放入Object[]变量中,但对象仍为String[]。这就是它拒绝Integer值的原因。您必须创建一个新的Objects[]数组,并复制值。您可以使用System.arraycopy函数复制数据,但无法避免创建新数组。
发布于 2012-09-11 20:44:02
不,没有办法避免复制split返回的数组。
split返回的数组实际上是一个String[],它允许您将该数组赋给Object[]类型的变量。然而,它仍然是一个真正的String[],所以当您尝试在其中存储String以外的其他内容时,您将得到一个ArrayStoreException。
有关背景信息,请参阅Java Language Specification中的4.10.3. Subtyping among Array Types。
发布于 2019-02-08 07:11:52
这是Java开发人员几个月前讨价还价的结果。虽然这看起来很奇怪,但是这个功能对于很多方法都很重要,比如Arrays.sort (它也恰好在Collections.sort中被调用)。基本上,如果Xwhere X is some subclass of Object不被认为是一个子类型,那么任何接受Object[]作为参数的方法都将停止执行。例如,数组可能已经被修改过,在某些情况下它们是只读的,但随后问题变成了“何时?”
一方面,将已作为参数传递到方法中的数组设为只读可能会阻碍编码器进行原地修改的能力。另一方面,如果将数组作为参数传递时设置为例外,则仍允许编码器进行非法修改,例如,当调用方传递的是Integer数组时,存储字符串。
但是说“整型for example不是Object[]的子类型”的结果是一个危机,必须为Object[]和Integer[]创建一个单独的方法。通过这种逻辑的扩展,我们可以进一步说,必须为String[]、Comparable[]等创建一个单独的方法。每种类型的数组都需要一个单独的方法,即使这些方法在其他方面完全相同。
这正是我们拥有多态性的那种情况。
不幸的是,在这里允许多态确实允许尝试在数组中非法存储值,如果发生这样的情况,就会抛出ArrayStoreException。然而,这是一个很小的代价,并且和ArrayIndexOutOfBoundsException一样可以避免。
在大多数情况下,可以通过两种方式轻松地防止ArrayStoreException (尽管您无法控制其他人做什么)。
1)
在不知道对象的实际组件类型的情况下,不要尝试将对象存储在数组中。当你正在处理的数组被传递到方法中时,你不一定知道它来自哪里,所以你不能假设它是安全的,除非组件类型的类是final(即没有子类)。
如果数组是从method返回的,就像上面讨论的那样,了解该方法。有没有可能实际类型是返回类型的子类?如果是这样,您必须考虑到这一点。
2)
当您第一次初始化本地使用的数组时,请使用X[] blah = new X[...];或X[] blah = {...};或(从Java10开始) 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“所以在决定采用哪条路径时,应该考虑如何影响代码的其余部分。
我只想用一个关于泛型的注释来结束(正如在前面的回答中提到的)。泛型实际上同样能够让毫无戒心的程序员大吃一惊。考虑下面的代码片段(修改自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
https://stackoverflow.com/questions/12369957
复制相似问题