技术分享
🗒️Generic Array Creation,泛型数组创建时提示出错
00 分钟
2021-3-15
2024-6-29
type
status
date
summary
slug
tags
category
password
URL
icon

起因

前两天在刷leetcode时,遇到一个设计题,
,当时的想法就是创建一个链表数组,用链表来解决哈希冲突问题。 然而在编写时,编译器报错,如下图所示。
notion image

分析

为什么会出现编译错误呢?这里就是泛型数组的问题了。 在Java中,数组是支持协变(Covariant)的,而泛型是不可变(Invariant)的。当两者在一起的时候,就会出现问题。 我们先来看看协变和不可变的定义 协变:是指子类型关系在类型变换的作用下保持原样。 逆变:指的是子类型关系在类型变换的作用下发生逆转。 不可变:表示子类型关系在类型变换的作用下, 既没有协变的效果,也没有逆变的效果。 具体到Java中就是:假设Java中有两个类Animal和Cat,它们之间的关系是Cat是Animal的子类,通过它们构造出的数组分别是Cat[]和Animal[],如果这两个数组间的关系与原始的两个类相同,那么我们就说数组具有协变性,代码如下:
泛型是不可变的可以体现在如下代码中:

解决办法

我们可以使用ArrayList代替数组来解决该问题,但是,如果我就是想用数组呢?该怎么处理呢? 既然泛型不支持协变,那我们就不使用泛型,直接使用原始类型,代码如下:
还有一种方式,就是使用泛型的通配符。有效代码如下所示
那为什么这种方式就是有效的呢? 首先,具有协变性的数组存在一个问题,如下代码所示:
所以,具有协变性质的数组存在以上的安全问题,所以泛型为了安全,不支持协变性。但是,有的时候为了兼容一些老的代码,我们还需要使用协变性,所以Java的设计者们提出了一种安全的协变方式,就是使用通配符。
使用通配符,类型变为了LinkedList<?>,这个类型通过类型擦除,会变为原始类型LinkedList,而原始类型是所有类型的父类型,我们可以理解为是LinkedList\<Object>类型,这样就不会存在ArrayStoreException的问题了,但是由于生成的是LinkedList\<Object>类型,我们还需要进行强制类型转换。
注意: 原始类型与Object参数化类型的区别: 原始类型可以表示持有任意类型的对象,在这一点上Object参数化类型也是如此,但是它们之间还存在一些区别。
  • 原始类型不进行类型检查, 而Object参数化类型则是明确告知编译器它持有的是任意类型
  • 原始类型是所有参数化类型的父类型,而后者并不能作为所有参数化类型的父类型,这也是为什么我们不能用new LinkedList\<Object>[5]代替new LinkedList\<?>的原因
参考文献:
上一篇
删除文件夹下的指定格式的文件(Java实现)
下一篇
将springboot项目升级到HTTP2