Java 为什么我们可以使用带有泛型引用的数组

Java 为什么我们可以使用带有泛型引用的数组,java,generics,jakarta-ee,scjp,Java,Generics,Jakarta Ee,Scjp,在回答有关此问题时: 我试着做到以下几点: Comparator<String>[] comparators = new Comparator[] {...}; 比较器[]比较器=新比较器[]{…}; 它起作用了!但以下情况并非如此: Comparator<String>[] comparators = new Comparator<String>[] {...}; 比较器[]比较器=新比较器[]{…}; 关于相关问题,我做出了如下假设: 我猜这是因

在回答有关此问题时:

我试着做到以下几点:

Comparator<String>[] comparators = new Comparator[] {...};
比较器[]比较器=新比较器[]{…};
它起作用了!但以下情况并非如此:

Comparator<String>[] comparators = new Comparator<String>[] {...};
比较器[]比较器=新比较器[]{…};

关于相关问题,我做出了如下假设:

我猜这是因为最初数组契约可能是 像这样:

如果您创建一个类型为X的数组,您将永远无法将 任何不是X的东西。如果你尝试,你会得到一个ArrayStoreException

因此,允许创建带有泛型的数组将导致不同的结果 规则如下:

如果创建类型为
X
的数组,则永远无法 把任何不是X的东西放进去。如果你尝试,你会得到一个X ArrayStoreException。但是由于类型擦除,您可以同时添加
X
X
对象


但想想看,这真的会是一个问题吗:

Comparator<String>[] comparators = new Comparator<String>[] {...};
比较器[]比较器=新比较器[]{…};
我真的不明白为什么不可能,因为使用这样的东西会:

  • 检查运行时插入的类
  • 检查编译时插入的类类型
最后,我们可以使用泛型类型引用的数组,因为不可能创建泛型类型的数组,我想很多人甚至不知道这是可能的

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

这有点像强迫人们使用
List=newarraylist()而不是使用
List=new ArrayList()


dimitrisli你给了Joshua Bloch的名著一个很好的例子。 正如您/他所解释的,使用泛型数组+协方差是危险的,并且可能导致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);
List[]stringLists=新列表[1];
List intList=Arrays.asList(42);
对象[]对象=字符串列表;
对象[0]=intList;
字符串s=StringList[0]。获取(0);

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

我知道您是从哪里来的(从实际意义上说,我基本上同意),但我认为有一个不同点促使了当前的情况

正如您提到的,擦除意味着泛型参数在运行时不可用,因此在编译时检查类型(可以是针对
列表的
或您的
比较器[]
)。关键的是,这是基于变量的通用参数

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

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

这一选择背后的根本原因,以及为什么数组在这方面与集合不同,是数组在插入时需要实际检查其参数的类型,然而,集合只是相信你的话,允许类型错误冒泡进入
ClassCastException

引用第120页:

为什么泛型数组创建是非法的-不会编译

List[]stringLists=新列表[1];//(1)
List intList=Arrays.asList(42);//(2)
对象[]对象=字符串列表;//(3)
对象[0]=intList;//(4)
字符串s=stringLists[0]。获取(0);//(5)
让我们假设创建泛型数组的第1行是合法的。第2行创建和 初始化包含单个元素的
列表。第3行存储
List
将数组放入对象数组变量中,这是合法的,因为数组 它们是协变的。第4行将
列表
存储到 对象数组,因为泛型是通过擦除实现的:
列表
实例的运行时类型仅为List,而
List[]
实例是
List[]
,因此此赋值不会生成
ArrayStoreException
。现在我们有麻烦了。我们存储了一个
列表
实例放入声明为仅包含<代码>列表实例的数组中。在里面 第5行,我们从这个数组中的sole列表中检索sole元素。编译程序 自动将检索到的元素强制转换为字符串,但它是一个整数,因此 运行时的
ClassCastException
。为了防止这种情况发生,第1行
(创建泛型数组)生成编译时错误。

可能有重复的人已经问过同样的问题@DaveWebb我不认为这是完全相同的问题-链接的问题是询问编译错误以及如何解决它,而这是询问语言设计背后的动机哲学。阅读第一个答案。这里有一个链接,详细解释了这里发生的事情。这个链接为你的问题提供了一些答案谢谢,我想你已经指出了:问题可能与数组协方差有关Hanks,很好的例子,我编辑了我的问题。请注意,我们仍然可以使用List[]stri
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)