一、Java 如果没有泛型会有什么灾难?
泛型其实玩的是「约束」,咱们程序界有一种说法叫做「约定大于配置」,其实老四觉得这个说法也同样适用于泛型的设计。我们都知道泛型最开始是给集合设计的,目的就是让集合记住自己存储了什么,约定其只存储某种特定的数据类型集,这样方便用于数据处理。那么反过来,在没有泛型之前,一个集合里面可能会因为各种各样的原因,什么样的数据类型、对象都可能存到集合里面,存没问题,但是取出来用就是满天星、遍地坑了。所以说到这里,也又对「约定大于配置」加深了一点理解。
二、List<? extends T> 和 List<? super T> 有哪些区别?
在说区别之前,我们需要知道泛型类型通配符的概念,泛型通配符的目的是为了解决类似于 List<String> 是可以理论上继承 List<Object>的问题,但是实际上不是这么用的,而是需要使用类型通配符「?」。这样的话就可以约定泛型属于某种特定的数据类型之下或者之上,老四也觉得是一种「约定大于配置」的设计思想。
那么 List<? extends T> 和 List<? super T> 分别代表的就是泛型类型通配符的上下限,用处也是各有不同。List<? extends T> 属于上限,要求子类型必须是 T 下面才能存放到集合列表当中,比如 T 代表 Number,那么 List<String> 就不允许了。List<? super T> 属于下限,要求在使用过程中要指定 T 属于当前对象的类型或者是父类型。
那么在使用场景上面,上限是用来读取集合数据的,下限用来写入数据的。因为上限能确定子类型是否归属,但如果写入的话就不能确定具体类型,最终都会向上转型变成 T,这在数据处理中是不严格的。而下限的设计估计就是为了解决集合写入的问题,因为使用下限的时候,可以明确当前操作的数据类型或者对象,以 T 作为主体来操作。
他俩的区别早就有前辈总结过,孤尽老师也在《手册》中提及到过,那就是「PECS(Producer Extends,Consumer Super)原则」,如果泛型表示一个生产者,咱们就使用 <? extends T>;如果泛型表示一个消费者,就使用 <? super T>。这里面的消费者其实代表的就是插入增加这类的操作的对象,而生产者代表就是读取内容的对象。我也总结了两句话:
- 如果你想遍历集合,需要对集合(生产者)里面的元素进行业务操作,那么就使用通配符上限(? extends T)
- 如果你想将外部的元素添加到某个集合(消费者)当中,那么就使用通配符下限(? super T)
三、类名 <? super T> 存在哪些实际应用场景?(参考Comparator)
如上所述,提及到生产者与使用者,那么自然在队列中就存在通配符上限的使用,比如在使用队列中间件,ActivityMq、RabbitMq 等过程中,使用泛型通配符是经常的事情。另外老四想到了反射中的一个 API:
Class<? super T> getSuperclass():返回该 Class 对象对应类的父类的 Class 对象。
四、相关文章阅读
- 浅析Java中的泛型以及泛型中的PECS(Producer Extends,Consumer Super)原则
- 浅析Java反射系列相关进阶知识(下)之JDK动态代理及反射泛型
- 《阿里巴巴 Java 开发设计手册系列》
更博不易,如果觉得文章对你有帮助并且有能力的老铁烦请捐赠盒烟钱,点我去赞助。或者扫描文章下面的微信/支付宝二维码打赏任意金额(点击「给你买杜蕾斯」),也可扫描小站放的支付宝领红包二维码,线下支付享受优惠的同时老四也可以获得对应赏金,老四这里抱拳谢谢诸位了。捐赠时请备注姓名或者昵称,因为您的署名会出现在赞赏列表页面,您的捐赠钱财也会被用于小站的服务器运维上面,再次抱拳感谢。