2012-03-27 47 views
6

在回答记者提问关于这里:https://stackoverflow.com/a/9872630/82609为什么我们可以使用阵列的通用参考

我试着做到以下几点:

Comparator<String>[] comparators = new Comparator[] {...}; 

它的工作原理!但是,下列情况不:

Comparator<String>[] comparators = new Comparator<String>[] {...}; 

论相关的问题,我的假设:

我想这是因为最初的阵列的合同可能是一些 这样的:

如果您创建了一个X类型的数组,那么您将永远无法将 中的任何东西放入IS-NOT-AN X中。如果您RY,你会得到一个ArrayStoreException信息

因此允许使用泛型创建阵列会导致像不同 规则:如果您创建X<Y>类型的数组

,你永远不会能够到 把任何不是一个X.如果你尝试,你会得到一个 ArrayStoreException。但是,由于类型擦除,您可以添加X<Y>X<Z>对象!


但想着它,将它确实是一个问题有:

Comparator<String>[] comparators = new Comparator<String>[] {...}; 

我真的不明白为什么它是不可能的,因为使用这样的事情:

  • 检查运行时插入的类
  • 检查在编译时

插入最后,我们可以使用与一般类型的参考和,因为不可能创建一个泛型类型的数组的数组类型,我想很多人甚至不知道这是可能的。

我只是想知道是否有人知道背后的原因?

这是一个有点像强迫人们使用List<String> = new ArrayList();,而不是使用List<String> = new ArrayList<String>();


dimitrisli你给约书亚布洛赫的名著一个很好为例。 正如你所解释的那样,使用通用数组+协方差都是危险的,并且可能导致ClassCastException,而我们期望使用协方差数组的ArrayStoreException。

但请注意以下几点仍然是合法的,并导致相同:

List<String>[] stringLists = new List[1]; 
List<Integer> intList = Arrays.asList(42); 
Object[] objects = stringLists; 
objects[0] = intList; 
String s = stringLists[0].get(0); 

但是它产生在编译时未检查铸警告,因为你mentionned,在运行一个ClassCastException。

+0

可能重复的[错误通用数组创建](http://stackoverflow.com/questions/3903196/error-generic-array-creation) – 2012-03-27 09:16:25

+0

@DaveWebb什么是重复的? – 2012-03-27 09:19:21

+0

有人已经问过同样的问题。 http://stackoverflow.com/questions/3903196/error-generic-array-creation – 2012-03-27 09:24:45

回答

4

我明白你来自哪里(实际意义上我基本同意),但我认为有一个区别激发了目前的情况。

如上所述,擦除意味着通用参数在运行时不可用,因此在编译时检查类型(对于List<String>Comparator<String>[])。关键是,这是基于变量的通用参数。

另一方面,阵列在运行时检查它们的参数类型,当它们被插入时,如果它们被误用(通常是由于滥用它们的协变性),它们可以抛出ArrayStoreException。因此数组需要能够在内部执行两个子弹点检查,当然他们不能在运行时检查通用参数。因此,实例化泛型数组是没有意义的,因为数组必须完全忽略泛型参数,因为这样会导致误导。

也就是说,将这样的数组分配给参数化的引用确实有意义,因为编译器可以执行通用检查。你认为这涵盖了所有的基础,并确保检查泛型类型(只要变量参数设置正确)。

这个选择背后的原因,以及为什么数组在这方面不同于集合,是因为数组在插入时需要实际检查它们的参数类型,而集合只是把你的单词用于它会允许类型错误在稍后进入ClassCastException

+0

谢谢,我想你指出了:问题可能是关于数组协变 – 2012-03-27 09:32:21

1

从大Effective Java Second Edition页面引用120:

为什么通用阵列的创建是非法的 - 将无法编译!

List<String>[] stringLists = new List<String>[1]; // (1) 
List<Integer> intList = Arrays.asList(42); // (2) 
Object[] objects = stringLists; // (3) 
objects[0] = intList; // (4) 
String s = stringLists[0].get(0); // (5) 

让我们假设1号线,这将创建一个通用的阵列,是合法的。第2行创建并且 初始化包含单个元素的List<Integer>。第3行将 List<String>数组存储到Object数组变量中,这是合法的,因为数组 是协变的。线4个存储List<Integer>到 对象阵列的鞋底元件,其成功,因为泛型是通过擦除实现:和的 运行时类型一个List<String>[]实例是List[]一个List<Integer>实例的 运行时类型是简单地列出,所以这分配不会生成 ArrayStoreException。现在我们遇到了麻烦。我们已经将一个List<Integer> 实例存储到声明为仅保存List<String>实例的数组中。在第5行的 中,我们从此阵列中的唯一列表中检索唯一元素。编译器 自动将检索到的元素转换为字符串,但它是一个整数,所以我们在运行时得到 a ClassCastException。为了防止发生这种情况,第1行 (它创建一个通用数组)会生成编译时错误。

+0

谢谢,很好的例子,我编辑我的问题。请注意,我们仍然可以使用列表 [] stringLists = new List [1];相反,它工作正常。你知道为什么允许吗? – 2012-03-27 09:43:26

+1

这是一个类型安全警告,因为我们尝试使用原始签名List []创建并将其分配到通用列表 []。尽管我们将其更改为列表 [],但编译器会抱怨以避免所描述的情况。 – dimitrisli 2012-03-27 10:13:34

相关问题