为什么我们可以使用带通用引用的数组

Seb*_*ber 6 java generics scjp java-ee

在回答有关此问题的问题时:https://stackoverflow.com/a/9872630/82609

我试着做以下事情:

Comparator<String>[] comparators = new Comparator[] {...};
Run Code Online (Sandbox Code Playgroud)

有用!但以下不是:

Comparator<String>[] comparators = new Comparator<String>[] {...};
Run Code Online (Sandbox Code Playgroud)

在相关问题上,我做了以下假设:

我想这是因为最初的数组合约可能是这样的:

如果你创建一个X类型的数组,你将永远不能在其中放入任何IS-NOT-AN X.如果你尝试,你会得到一个ArrayStoreException

因此,允许使用泛型创建的数组会导致不同的规则,例如:

如果你创建一个类型的数组X<Y>,你将永远不能放置任何IS-NOT-AN X.如果你尝试,你将得到一个ArrayStoreException.但是你可以添加两个X<Y>X<Z>对象,因为类型擦除!


但是考虑一下,它真的会成为一个问题:

Comparator<String>[] comparators = new Comparator<String>[] {...};
Run Code Online (Sandbox Code Playgroud)

我真的不明白为什么它不可能,因为使用这样的东西会:

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

最后,我们可以使用具有泛型类型引用的数组,并且由于不可能创建具有泛型类型的数组,我想很多人甚至不知道它是可能的.

我只是想知道是否有人知道这个选择背后的原因?

这有点像强迫人们使用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);
Run Code Online (Sandbox Code Playgroud)

但是,它会在编译时生成未经检查的强制转换警告,正如您所提到的,在运行时会产生ClassCastException.

And*_*yle 4

我明白你的观点(从实际意义上讲,我基本上同意),但我认为存在差异,导致了当前的情况。

正如您所提到的,擦除意味着通用参数在运行时不可用,因此在编译时检查类型(无论是 aList<String>还是您的Comparator<String>[])。重要的是,这是基于变量的通用参数。

另一方面,数组在插入时会在运行时检查其参数的类型,因此ArrayStoreException如果误用(通常是由于滥用协方差),它们可能会抛出异常。因此,数组需要能够在内部执行这两个要点检查,当然它们无法在运行时检查通用参数。因此,实例化泛型数组是没有意义的,因为该数组必须完全忽略泛型参数,这充其量只会产生误导。

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

这种选择背后的根本原因,以及为什么数组在这方面与集合不同,是数组需要在插入时实际检查其参数的类型,而集合只是接受您的话并允许类型错误会冒泡到ClassCastException以后。