Java 泛型通配符

为何要使用泛型通配符

根据里氏替换原则,我们通常是用基类的引用指向子类对象,例如

class Shape {}
class Circle extends Shape {}
class Square extends Shape {}

Shape shape = new Circle();

对于泛型来说,下面的代码是错误的

// 不支持这种泛型语法
List<Shape> shapes = new ArrayList<Cirlce>();

这种泛型语法被定义为错误的语法。由于泛型擦除,在运行时,系统并不知道 shapes 保存的对象的类型,那何谈确保添加正确类型的对象呢?

泛型通配符上界

如果我们仍然想建立这种关系,可以使用泛型通配符,由问号表示

List<? extends Shape> shapes = new ArrayList<Circle>();

? extends Shape 定义了泛型的下界,也就是 Shape 类或者它的子类,但是我们似乎并不关心是什么子类型,这里我们只想要把 shapes 指向一个泛型类型为 Circle 的 ArrayList 对象。

但是由于泛型擦除,编译期和运行期,都无法知道 shapes 到底保存了哪个类型,那么怎么能安全地向其中添加对象呢? 因此Java拒绝这种做法。例如下面代码是错误的

List<? extends Shape> shapes = new ArrayList<Circle>();
// 编译报错
shapes.add(new Shape());

这个错误我们完全可以解释的,add 方法的原型如下

boolean add(T t);

add 方法的参数类型是一个泛型,在编译期,编译器会在这里添加一个边界动作,也就是检测类型是否正确。 然而,此时编译器只知道泛型的类型为 ? extends Shape ,但是并不知道具体是哪个类型,那么就无法确保添加对象的类型正确。 因此这种语法被拒绝了。

List<? extends Shape> shapes 连对象的无法添加,那么它存在的意义是什么呢? 由于 List<? extends Shape> 像是一个基类,因此用在方法的参数类型

boolean isExists(List<? extends Shape> list, Shape obj) {
	list.foreach(System.out::println);
	return list.contains(obj);
}

从这个方法可以看出,我们可以对List<? extends Shape> list 进行遍历,这应该是理所当然的,但是遍历对象的类型只能是 Shape。

然而居然可以对 List<? extends Shape> list 调用 contains 方法,我们刚在前面说过不能调用 add 方法,为何? 可以看下 contains 方法的原型

boolean contains(Object obj);

原来它的参数类型是 Object,这样就不用编译器来确保类型的正确, 因此List<? extends Shape> list 调用 contains 方法,也是理所当然的。

泛型通配符下界

? extends Shape 表示了泛型通配符的的上界是 Shape, 而 ? super Cirlce 表示的泛型通配符的下界是 Circle 的基类。因此可以向这个集合中添加元素

注意,泛型类的泛型参数不能使用<T super MyClass> 这种形式,但是在泛型类的内部可以使用 <? super T>。

List<? super Circle> circles = new ArrayList<>();
circles.add(new Circle();

? super Circle 只是指定了下界,由于泛型擦除,我们并不知道具体保存了什么类型,所以从 List<? super Circle> circles 获取的元素只能是 Object 类型,如下

List<? super Circle> circles = new ArrayList<>();
circles.add(new Circle()

// 获取的元素只能是 Object 类型
Object obj = circles.get(0);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值