Java泛型上下限
前言
Java中支持泛型机制。泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数。
泛型信息只存在于代码编译阶段,在Java的运行期(已经生成字节码文件后)会被擦除掉,专业术语叫做类型擦除。
Java 泛型中的 extends
和 super
是用来限制泛型类型参数的上限和下限的关键字。
原文地址:https://xuedongyun.cn/post/3373/
T的用法
- 可以作用在类上
1 | class Test<T> { |
- 也可以作用在方法上
1 | class Test { |
- 还可以使用
extends
限制:T类型为某类型的子类
1 | public <T extends Number> void test(T number) { |
?的用法
<? extends T>
:是指 “上界通配符”<? super T>
:是指 “下界通配符”
这两个用法令我们在使用带泛型类型的容器时,能很方便的处理容器内部泛型类型的多态问题
为什么使用
我们为什么要使用通配符和边界呢?我们来看一个例子:
假设我们的继承关系如下:
1 | class Food { } |
现在我们有一个盘子类,可以用来装水果
1 | class Plate<T> { |
理论上,我们有一个装水果的盘子,当然也能用来装苹果对吧?然而事实是,Java编译器会报错。即:即使容器里装的东西有继承关系,但容器之间并没有继承关系。
1 | Plate<Fruit> applePlate = new Plate<Apple>(); // 编译器报错 |
为了让容器的泛型用起来更舒服,<? extends T>
和<? super T>
应运而生
上界(extends)
<? extends T>
:表示只能存放T的子类型(包括T)
1 | Plate<? extends Fruit> plate = new Plate<Apple>(); |
上界不能往里存,只能往外取(以父类作为类型)
1 | Plate<? extends Fruit> plate = new Plate<Apple>(); |
原因:
编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道。可能是Fruit?可能是Apple?也可能是Banana?即使编译器在看到后面用Plate赋值以后,盘子里也没有被标上有“Apple”。而是标上一个占位符:CAP#1,来表示捕获一个Food或Food的子类,具体是什么类不知道。无论是想往里插入Apple或者Fruit或者Food编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。
这是为了泛型安全,因为其会在编译期间生成桥接方法
<Bridge Methods>
该方法中会出现强制转换,若出现多种子类型,则会强制转换失败虽然不能添加元素,但是其中的元素都有一个共性:有相同的父类。所以可以在获取元素时,统一转化为父类取出。
下界(super)
<? super T>
:表示只能存放T的父类型(包括T)
下界能往里存,但往外取只能取Object
1 | Plate<? super Fruit> plate = new Plate<Food>(); |
由于存放的都是T的父类(包括T),添加元素时,只能向其添加T的子类,才能保证转化为T类型时是类型安全的。
但其中的元素类型众多,在获取元素时我们无法判断是哪一种类型,只有所有类的基类Object对象才能装下。
PECS原则
Producer Extends Consumer Super
- 频繁往外读取内容的,适合用上界extends
- 经常往里插入的,适合用下界super
方便容器使用
1 | // 只能往外读,读取类型为父类 |
- 标题: Java泛型上下限
- 作者: 布鸽不鸽
- 创建于 : 2023-07-16 16:58:36
- 更新于 : 2023-07-16 17:06:56
- 链接: https://xuedongyun.cn//post/3373/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。