在编译时捕获ArrayStoreException
考虑以下Java ArrayList#toArray
方法的测试。 请注意,我从这个有用的答案中借用了代码。
public class GenericTest { public static void main(String [] args) { ArrayList foo = new ArrayList(); foo.add(1); foo.add(2); foo.add(3); foo.add(4); foo.add(5); Integer[] bar = foo.toArray(new Integer[10]); System.out.println("bar.length: " + bar.length); for(Integer b : bar) { System.out.println(b); } String[] baz = foo.toArray(new String[10]); // ArrayStoreException System.out.println("baz.length: " + baz.length); } }
但是,请注意,在尝试将Integer
放入String[]
时会出现ArrayStoreException
。
输出:
$>javac GenericTest.java && java -cp . GenericTest bar.length: 10 1 2 3 4 5 null null null null null Exception in thread "main" java.lang.ArrayStoreException at java.lang.System.arraycopy(Native Method) at java.util.ArrayList.toArray(Unknown Source) at GenericTest.main(GenericTest.java:16)
编译时可以通过Javagenerics来防止此错误吗?
ArrayStoreException
存在正是因为Java的类型系统无法正确处理这种情况(IIRC,当Generics出现时,以与集合框架相同的方式改造数组为时已晚)。
因此,在编译时通常无法阻止此问题。
您当然可以创建内部API来包装此类操作,以减少意外获取类型错误的可能性。
也可以看看:
- 处理ArrayStoreException
- 为什么数组是协变的但是generics是不变的?
List#toArray(T[])
是声明为的generics方法
T[] toArray(T[] a);
因此,类型参数可以从给定数组的类型推断,也可以使用
符号来为方法调用添加前缀。
所以你可以做到
String[] baz = foo.toArray(new String[10]); // doesn't compile
但我认为这是你能做的最好的事情。
但从这个意义上说,你可以清楚地看到Integer
与String
不匹配(反之亦然)。
请注意,这是一个记录在案的exception
ArrayStoreException – 如果指定数组的运行时类型不是此列表中每个元素的运行时类型的超类型
所以我认为你不应该在编译时试图找到它。
出于兼容性原因,无法更改Collection.toArray
方法。
但是,对于您自己的代码,您可以创建一个(更多)类型安全的帮助器方法,如果您因此使用您的方法,它将保护您免受ArrayStoreException
:
public static T[] toArray(List extends T> list, T[] t) { return list.toArray(t); }
此方法将拒绝String[] s=toArray(new ArrayList
这与您的问题的示例案例相匹配,但要注意数组子类型规则:它不会拒绝
Object[] s=toArray(new ArrayList(), new String[0]);
因为pre-Generics“ String[]
是Object[]
”规则的子类。 使用现有的Java语言无法解决这个问题。
ArrayStoreException
是运行时exception,不是编译时间,在运行时抛出, 它表示不同类型的对象存储在数组中。 Object x [] = new String [3]; x [0] = new Integer(0); 在编译时可以找到它的唯一方法是使用< Integer >类型,如下所示
foo.toArray(new String[10]);
以上将抛出编译时错误,因为The parameterized method
。