首页 > 编程知识 正文

super tv,泛型通配符?和泛型t区别

时间:2023-05-03 15:00:19 阅读:114782 作者:625

简述

在平时的工作学习中,一定见过很多下面这样的词语

List super T

列表扩展t

上面的代码知道Java的通用性,这两种不同的写法有什么区别呢?

首先,说到Java的通用性,必须提到Java通用型删除机构: Java的通用性基本上是在编译器这个层面上实现的。生成的Java字节码中不包含通用型的类型信息。使用通用型时追加的类型信息此过程称为类型擦除。 代码中定义的类型(如List和List )在编译后变为List。 JVM只能看到List,通用添加的类型信息对于JVM来说是不可见的。

使用通用类时,可以使用具体类型。 例如,可以定义List中的对象。 通用参数为集成器; 也可以使用通配符吗? 表示未知类型。 例如,List表示具有通用参数的类型,但不知道其具体类型是什么。

请注意,List声明所有类型都是可能的,但List与List不同。 对List来说,List实际上包含的是Object及其子类,可以使用Object类型接收其元素。 相反,List表示其中包含的元素类型不确定。 其中可能包含String或Integer。 如果包含字符串,则在其中添加Integer类型的元素是错误的。 作为比较,可以在List中添加字符串元素,也可以添加Integer类型的元素。 因为这些都是Object的子类。

编译器无法使用new ArrayList () )方法创建新的ArrayList对象,因为编译器不知道特定类型是什么。 但是,对于List中的元素,类型未知,但它是Object及其子类,因此可以使用Object来接收。

如上所述,List中的元素只能使用Object引用。 虽然这并不是很有用,但幸运的是,Java的通用机制允许对通用参数类型的上限和下限进行限制。 例如,列表扩展编号将通用上限定义为编号。 也就是说,List中包含的要素类型是Number及其子类。另一方面,List super Number定义了通用的下界,也就是说,List中包含的是Number及其父类。

引入通用参数的上界和下界后,我们写代码相对方便,但引入了新的问题。 也就是说,我们什么时候使用上界,什么时候使用下界,其区别和限制到底是什么? 谈谈我的理解吧。

? 扩展t

? extends T描述了通配符上的边界。 也就是说,特定通用参数必须符合条件的:通用参数必须是t型或其子类,例如:

listextendsnumbernumberarray=new ArrayList (; //Number是编号类型

listextendsnumbernumberarray=new ArrayList (; //Integer是Number的子类

listextendsnumbernumberarray=new ArrayList (; //Double是Number的子类

上面三个操作都是合法的。 为什么? extends Number规定了通用通配符的上限。 这意味着我们的实际通用必须是Number类型或其子类,而Number、Integer和Double显然是Number的子类。 模子一样也可以。 也就是说,这里Number被认为是Number的子类。

子类型判定

假设有两个类:类型g、SuperClass和SubClass,并且SuperClass是SubClass的父类,则:

G extends SubClass是G extends SuperClass的子类型。 例如,列表扩展整数是列表扩展编号的子类型

g是G extends SuperClass的子类型,例如List是List extends Integer的子类型。

与G extends Object等效。

可以想象,G extends T是左闭右开的区间,G extends Object是最大的区间。 如果区间G extends SuperClass包含区间G extends SubClass

, 那么较大的区间就是父类.

关于读取

根据上面的例子, 对于 List extends Number> numberArray 对象:

我们能够从 numberArray 中读取到 Number 对象, 因为 numberArray 中包含的元素是 Number 类型或 Number 的子类型.

我们不能从 numberArray 中读取到 Integer 类型, 因为 numberArray 中可能保存的是 Double 类型.

同理, 我们也不能从 numberArray 中读取到 Double 类型.

关于写入

根据上面的例子, 对于 List extends Number> numberArray 对象:

我们不能添加 Number 到 numberArray 中, 因为 numberArray 有可能是List 类型

我们不能添加 Integer 到 numberArray 中, 因为 numberArray 有可能是 List 类型

我们不能添加 Double 到 numberArray 中, 因为 numberArray 有可能是 List 类型

即, 我们不能添加任何对象到 List extends T> 中, 因为我们不能确定一个 List extends T> 对象实际的类型是什么, 因此就不能确定插入的元素的类型是否和这个 List 匹配. List extends T> 唯一能保证的是我们从这个 list 中读取的元素一定是一个 T 类型的.

? super T

? super T 描述了通配符下界, 即具体的泛型参数需要满足条件: 泛型参数必须是 T 类型或它的父类, 例如:

// 在这里, Integer 可以认为是 Integer 的 "父类"

List super Integer> array = new ArrayList();

// Number 是 Integer 的 父类

List super Integer> array = new ArrayList();

// Object 是 Integer 的 父类

List super Integer> array = new ArrayList();

关于读取

对于上面的例子中的 List super Integer> array 对象:

我们不能保证可以从 array 对象中读取到 Integer 类型的数据, 因为 array 可能是 List 类型的.

我们不能保证可以从 array 对象中读取到 Number 类型的数据, 因为 array 可能是 List 类型的.

唯一能够保证的是, 我们可以从 array 中获取到一个 Object 对象的实例.

关于写

对于上面的例子中的 List super Integer> array 对象:

我们可以添加 Integer 对象到 array 中, 也可以添加 Integer 的子类对象到 array 中.

我们不能添加 Double/Number/Object 等不是 Integer 的子类的对象到 array 中.

易混淆点

有一点需要注意的是, List super T> 和 List extends T> 中, 我们所说的 XX 是 T 的父类(a superclass of T) 或 XX 是 T 的子类(a subclass of T) 其实是针对于泛型参数而言的. 例如考虑如下例子:

List super Integer> l1 = ...

List extends Integer> l2 = ...

那么这里 ? super Integer 和 ? extends Integer 的限制是对谁的呢? 是表示我们可以插入任意的对象 X 到 l1 中, 只要 X 是 Integer 的父类? 是表示我们可以插入任意的对象 Y 到 l2 中, 只要 Y 是 Integer 的子类?

其实不是的, 我们必须要抛弃上面的概念, ? super Integer 和 ? extends Integer 限制的其实是 泛型参数, 即 List super Integer> l1 表示 l1 的泛型参数 T 必须要满足 T 是 Integer 的父类, 因此诸如 List, List

令 T 是 l1 的泛型参数, 即:

l1 = List = List super Integer>

因此有 T 是 Integer 或 Integer 的父类.

如果 T 是 Integer, 则 l1 = List, 显然我们可以添加任意的 Integer 对象或 Integer 的子类对象到 l1 中.

如果 T 是 Integer 的父类, 那么同理, 对于 Integer 或 Integer 的子类的对象, 我们也可以添加到 l1 中.

按同样的分析方式, List extends Integer> l2 表示的是 l2 的泛型参数是 Integer 的子类型. 而如果我们要给一个 List 插入一个元素的话, 我们需要保证此元素是 T 或是 T 的子类, 而这里 List extends Integer> l2, l2 的泛型参数是什么类型我们都不知道, 进而就不能确定 l2 的泛型参数的子类是哪些, 因此我们就不能向 l2 中添加任何的元素了.

来一个对比:

对于 List super Integer> l1:

正确的理解: ? super Integer 限定的是泛型参数. 令 l1 的泛型参数是 T, 则 T 是 Integer 或 Integer 的父类, 因此 Integer 或 Integer 的子类的对象就可以添加到 l1 中.

错误的理解: ? super Integer限定的是插入的元素的类型, 因此只要是 Integer 或 Integer 的父类的对象都可以插入 l1 中

对于 List extends Integer> l2:

正确的理解: ? extends Integer 限定的是泛型参数. 令 l2 的泛型参数是 T, 则 T 是 Integer 或 Integer 的子类, 进而我们就不能找到一个类 X, 使得 X 是泛型参数 T 的子类, 因此我们就不可以向 l2 中添加元素. 不过由于我们知道了泛型参数 T 是 Integer 或 Integer 的子类这一点, 因此我们就可以从 l2 中读取到元素(取到的元素类型是 Integer 或 Integer 的子类), 并可以存放到 Integer 中.

错误的理解: ? extends Integer 限定的是插入元素的类型, 因此只要是 Integer 或 Integer 的子类的对象都可以插入 l2 中

使用场景

PECE 原则: Producer Extends, Consumer Super

Producer extends: 如果我们需要一个 List 提供类型为 T 的数据(即希望从 List 中读取 T 类型的数据), 那么我们需要使用 ? extends T, 例如 List extends Integer>. 但是我们不能向这个 List 添加数据.

Consumer Super: 如果我们需要一个 List 来消费 T 类型的数据(即希望将 T 类型的数据写入 List 中), 那么我们需要使用 ? super T, 例如 List super Integer>. 但是这个 List 不能保证从它读取的数据的类型.

如果我们既希望读取, 也希望写入, 那么我们就必须明确地声明泛型参数的类型, 例如 List.

例子:

public class Collections {

public static void copy(List super T> dest, List extends T> src)

{

for (int i=0; i

dest.set(i,src.get(i));

}

}

上面的例子是一个拷贝数据的代码, src 是 List extends T> 类型的, 因此它可以读取出 T 类型的数据(读取的数据类型是 T 或是 T 的子类, 但是我们不能确切的知道它是什么类型, 唯一能确定的是读取的类型 is instance of T), , dest 是 List super T> 类型的, 因此它可以写入 T 类型或其子类的数据.

参考

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。