Generic Array Creation,泛型数组创建时提示出错。

Java的数组是协变的,但泛型是不变的,这导致泛型数组创建时的编译错误。为了解决这个问题,可以使用ArrayList或者采用泛型通配符如`LinkedList<?>[]`来创建安全的数组。通配符允许协变,同时避免ArrayStoreException。原始类型和Object参数化类型在安全性上有区别,前者不进行类型检查,后者明确告知编译器持有任意类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

泛型数组为什么会出现编译错误呢?

在 Java中,数组是支持协变(Covariant)的,而泛型是不可变(Invariant)的。当两者在一起的时候,就会出现问题。

  • 协变:是指子类型关系在类型变换的作用下保持原样。
  • 逆变:指的是子类型关系在类型变换的作用下发生逆转。
    不可变:表示子类型关系在类型变换的作用下, 既没有协变的效果,也没有逆变的效果。

假设 Java 中有两个类 Animal 和 Cat,它们之间的关系是 Cat 是 Animal 的子类,通过它们构造出的数组分别是 Cat[] 和 Animal[],如果这两个数组间的关系与原始的两个类相同,那么就说数组具有协变性,代码如下:

Animal[] animals = new Cat[10] // 这种写法是可以通过编译的

泛型是不可变的可以体现在如下代码中:

HashSet<Object> set = new HashSet<String>(); // 无法通过编译,即使 Object 是 String的父类

解决办法

可以使用 ArrayList 代替数组来解决该问题,但是,如果就想用数组呢?
既然泛型不支持协变,那就不使用泛型,直接使用原始类型,代码如下:

LinkedList[] res = new LinkedList[20];
res[0] = new LinkedList();
res[0].add("23333");

还有一种方式,就是使用泛型的通配符。有效代码如下所示

LinkedList<String>[] arr = (LinkedList<String>[])new LinkedList<?>[5];

那为什么这种方式就是有效的呢?
首先,具有协变性的数组存在一个问题,如下代码所示:

Object[] arr = new Integer[20];   // 这句代码是可以正确通过编译检查的
arr[0] = new String("20");      //会抛出 java.lang.ArrayStoreException

所以,具有协变性质的数组存在以上的安全问题,所以泛型为了安全,不支持协变性。但是,有的时候为了兼容一些老的代码,还需要使用协变性,所以 Java 的设计者们提出了一种安全的协变方式,就是使用通配符。

使用通配符,类型变为了 LinkedList<?>,这个类型通过类型擦除,会变为原始类型LinkedList,而原始类型是所有类型的父类型,可以理解为是 LinkedList 类型,这样就不会存在 ArrayStoreException 的问题了,但是由于生成的是 LinkedList 类型,还需要进行强制类型转换。

注意:
原始类型与 Object 参数化类型的区别:
原始类型可以表示持有任意类型的对象,在这一点上 Object 参数化类型也是如此,但是它们之间还存在一些区别。

原始类型不进行类型检查, 而 Object 参数化类型则是明确告知编译器它持有的是任意类型。
原始类型是所有参数化类型的父类型,而后者并不能作为所有参数化类型的父类型,这也是为什么我们不能用 new LinkedList[5] 代替 new LinkedList<?> 的原因。

参考文献:

Java 中的协变与逆变
泛型进阶
generic-array-creation-not-allowed-in-java
泛型基础
generic-array-creation
generics-in-java
creating-a-generic-array
java-generics
generic-array-creation-error

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值