Java 泛型中的通配符
本文内容如下:
1、 什么是类型擦除
2、常用的 ?, T, E, K, V, N的含义
3、上界通配符 < ?extends E>
4、下界通配符 < ?super E>
5、什么是PECS原则
6、通过一个案例来理解 ?和 T 和 Object 的区别
一、什么是类型擦除?
我们说Java的泛型是伪泛型,那是因为泛型信息只存在于代码编译阶段,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程为类型擦除。
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是因为类型擦除特性,让泛型代码能够很好地和之前版本的代码兼容。
我们来看个案例
(图1)
因为这里泛型定义为Integer类型集合,所以添加String的时候在编译时期就会直接报错。
那是不是就一定不能添加了呢?答案是否定的,我们可以通过Java泛型中的类型擦除特点及反射机制实现。
如下
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList();
list.add(6);
//反射机制实现
Class<? extends ArrayList> clazz = list.getClass();
Method add = clazz.getDeclaredMethod("add", Object.class);
add.invoke(list, "欢迎关注:后端元宇宙");
System.out.println("list = " + list);
}
运行结果
list = [6, 欢迎关注:后端元宇宙]
二、案例实体准备
这里先建几个实体,为后面举例用
Animal类
@Data
@AllArgsConstructor
public class Animal {
/**
* 动物名称
*/
private String name;
/**
* 动物毛色
*/
private String color;
}
Pig类 :Pig是Animal的子类
public class Pig extends Animal{
public Pig(String name,String color){
super(name,color);
}
}
Dog类: Dog也是Animal的子类
public class Dog extends Animal {
public Dog(String name,String color){
super(name,color);
}
}
三、常用的 ?, T, E, K, V, N的含义
我们在泛型中使用通配符经常看到T、F、U、E,K,V其实这些并没有啥区别,我们可以选 A-Z 之间的任何一个字母都可以,并不会影响程序的正常运行。
只不过大家心照不宣的在命名上有些约定:
- T (Type) 具体的Java类
- E (Element)在集合中使用,因为集合中存放的是元素
- K V (key value) 分别代表java键值中的Key Value
- N (Number)数值类型
- ? 表示不确定的 Java 类型
四、上界通配符 < ? extends E>
语法:<? extends E>
举例:<? extends Animal> 可以传入的实参类型是Animal或者Animal的子类
两大原则
add:除了null之外,不允许加入任何元素!get:可以获取元素,可以通过E或者Object接受元素!因为不管存入什么数据类型都是E的子类型
示例
public static void method(List<? extends Animal> lists){
//正确 因为传入的一定是Animal的子类
Animal animal = lists.get(0);
//正确 当然也可以用Object类接收,因为Object是顶层父类
Object object = lists.get(1);
//错误 不能用?接收
? t = lists.get(2);
// 错误
lists.add(new Animal());
//错误
lists.add(new Dog());
//错误
lists.add(object);
//正确 除了null之外,不允许加入任何元素!
lists.add(null);
}
五、下界通配符 < ? super E>
语法: <? super E>
举例 :<? super Dog> 可以传入的实参的类型是Dog或者Dog的父类类型
两大原则
add:允许添加E和E的子类元素!get:可以获取元素,但传入的类型可能是E到Object之间的任何类型,也就无法确定接收到数据类型,所以返回只能使用Object引用来接受!如果需要自己的类型则需要强制类型转换。
示例
public static void method(List<? super Dog> lists){
//错误 因为你不知道?到底啥类型
Animal animal = lists.get(0);
//正确 只能用Object类接收
Object object = lists.get(1);
//错误 不能用?接收
? t = lists.get(2);
//错误
lists.add(object);
//错误
lists.add(new Animal());
//正确
lists.add(new Dog());
//正确 可以存放null元素
lists.add(null);
}
六、什么是PECS原则?
PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。
原则
- 如果想要获取,而不需要写值则使用" ? extends T "作为数据结构泛型。
- 如果想要写值,而不需要取值则使用" ? super T "作为数据结构泛型。
示例-
public class PESC {
ArrayList<? extends Animal> exdentAnimal;
ArrayList<? super Animal> superAnimal;
Dog dog = new Dog("小黑", "黑色");
private void test() {
//正确
Animal a1 = exdentAnimal.get(0);
//错误
Animal a2 = superAnimal.get(0);
//错误
exdentAnimal.add(dog);
//正确
superAnimal.add(dog);
}
}
示例二
Collections集合工具类有个copy方法,我们可以看下源码,就是PECS原则。
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
我们按照这个源码简单改造下
public class CollectionsTest {
/**
* 将源集合数据拷贝到目标集合
*
* @param dest 目标集合
* @param src 源集合
* @return 目标集合
*/
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
for (int i = 0; i < srcSize; i++) {
dest.add(src.get(i));
}
}
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList();
ArrayList<Pig> pigs = new ArrayList();
pigs.add(new Pig("黑猪", "黑色"));
pigs.add(new Pig("花猪", "花色"));
CollectionsTest.copy(animals, pigs);
System.out.println("dest = " + animals);
}
}
运行结果
dest = [Animal(name=黑猪, color=黑色), Animal(name=花猪, color=花色)]
七、通过一个案例来理解 ?和 T 和 Object 的区别
1、实体转换
我们在实际开发中,经常进行实体转换,比如SO转DTO,DTO转DO等等,所以需要一个转换工具类。
如下示例
/**
* 实体转换工具类
*
* TODO 说明该工具类不能直接用于生产,因为为了代码看去清爽点,我少了一些必要检验,所以如果直接拿来使用可以会在某些场景下会报错。
*/
public class EntityUtil {
/**
* 集合实体转换
*
* @param target 目标实体类
* @param list 源集合
* @return 装有目标实体的集合
*/
public static <T> List<T> changeEntityList(Class<T> target, List<?> list) throws Exception {
if (list == null || list.size() == 0) {
return null;
}
List<T> resultList = new ArrayList<T>();
//用Object接收
for (Object obj : list) {
resultList.add(changeEntityNew(target, obj));
}
return resultList;
}
/**
* 实体转换
*
* @param target 目标实体class对象
* @param baseTO 源实体
* @return 目标实体
*/
public static <T> T changeEntity(Class<T> target, Object baseTO) throws Exception{
T obj = target.newInstance();
if (baseTO == null) {
return null;
}
BeanUtils.copyProperties(baseTO, obj);
return obj;
}
}
使用工具类示例
private void changeTest() throws Exception {
ArrayList<Pig> pigs = new ArrayList();
pigs.add(new Pig("黑猪", "黑色"));
pigs.add(new Pig("花猪", "花色"));
//实体转换
List<Animal> animals = EntityUtil.changeEntityList(Animal.class, pigs);
}
这是一个很好的例子,从这个例子中我们可以去理解 ?和 T 和 Object的使用场景。
我们先以集合转换来说
public static <T> List<T> changeEntityListNew(Class<T> target, List<?> list);
首先其实我们并不关心传进来的集合内是什么对象,我们只关系我们需要转换的集合内是什么对象,所以我们传进来的集合就可以用List<?>表示任何对象的集合都可以。
返回呢,这里指定的是Class<T>,也就是返回最终是List<T>集合。
再以实体转换方法为例
public static <T> T changeEntityNew(Class<T> target, Object baseTO)
同样的,我们并不关心源对象是什么,我们只关心需要转换的对象,只需关心需要转换的对象为T。
那为什么这里用Object上面用?呢,其实上面也可以改成List<Object> list,效果是一样的,上面List<?> list在遍历的时候最终不就是用Object接收的吗
?和Object的区别
?类型不确定和Object作用差不多,好多场景下可以通用,但?可以缩小泛型的范围,如:List<? extends Animal>,指定了范围只能是Animal的子类,但是用List<Object>,没法做到缩小范围。
总结
- 只用于读功能时,泛型结构使用<? extends T>
- 只用于写功能时,泛型结构使用<? super T>
- 如果既用于写,又用于读操作,那么直接使用<T>
- 如果操作与泛型类型无关,那么使用<?>
声明: 公众号如需转载该篇文章,发表文章的头部一定要 告知是转至公众号: 后端元宇宙。同时也可以问本人要markdown原稿和原图片。其它情况一律禁止转载!
Java 泛型中的通配符的更多相关文章
- Java泛型中的通配符
Java泛型中的通配符可以直接定义泛型类型的参数.而不用把该函数定义成泛型函数. public class GenericsTest { public static void main(String[ ...
- Java泛型中的通配符T,E,K,V
Java泛型中的通配符T,E,K,V 1.泛型的好处 2.泛型中的通配符 2.1 T,E,K,V,? 2.2 ?无界通配符 2.3 上界通配符 < ? extends E> 2.4 下界通 ...
- 【转】聊一聊-JAVA 泛型中的通配符 T,E,K,V,?
原文:https://juejin.im/post/5d5789d26fb9a06ad0056bd9 前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型 ...
- JAVA 泛型中的通配符 T,E,K,V,?
前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据 ...
- 关于JAVA泛型中的通配符类型
之前对JAVA一知半解时就拿起weiss的数据结构开始看,大部分数据结构实现都是采取通配符的思想,好处不言而喻. 首先建立两个类employee和manager,继承关系如下.其次Pair类是一个简单 ...
- Java泛型中的通配符的使用
package com.srie.testjava; import java.util.ArrayList; import java.util.List; public class TestClass ...
- Java泛型中的协变和逆变
Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛 ...
- Java泛型中extends和super的区别?
<? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. ...
- Java编程的逻辑 (36) - 泛型 (中) - 解析通配符
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
随机推荐
- mycat实现主从读取中的问题
schema.xml 中的配置如下:..... <dataHost name="aaa" maxCon="2000" minCon="100&q ...
- 微信小程序循环列表点击每一个单独添加动画
首先,咱们看一下微信小程序动画怎么实现,我首先想到的是anime.js,但是引入之后用不了,微信小程序内的css也无法做到循环的动态,我就去找官方文档看看有没有相应的方法,哎,还真有 点击这里查看 微 ...
- 微信小程序一些标签
wxml标签 一.视图容器(View Container): 二.基础内容(Basic Content) 标签名 说明 标签名 说明 view 视图容器 icon 图标 scroll-view ...
- 超详细讲解H5移动端适配
前言 移动互联网发展至今,各种移动设备应运而生,但它们的物理分辨率可以说是五花八门,一般情况UI会为我们提供375尺寸的设计稿,所以为了让H5页面能够在这些不同的设备上尽量表现的一致,前端工程师就不得 ...
- 如何离线安装posh-git
不用上github 1.下载post-git离线安装包 地址:https://files.cnblogs.com/files/xcr1234/posh-git-master.zip 2,用Powers ...
- 20202127 实验二《Python程序设计》实验报告
20202127 2021-2022-2 <Python程序设计>实验二报告 课程:<Python程序设计>班级: 2021姓名: 马艺洲学号:20202127实验教师:王志强 ...
- Percona停服俄罗斯
2022年3月9日,MySQL重要分支Percona宣布,他们将停止与俄罗斯和白俄罗斯的组织开展新业务,直至另行通知. Percona为支持员工而采取的一些行动如下: 已经在乌克兰目前安全的部分获得了 ...
- 01. 树莓派初始配置——ssh连接使用root用户登录
ssh连接使用root用户登录 普通的"pi"用户没有权限访问某些文件夹,或者对它们只有只读功能.部分操作需要在每条命令前都要加上"sudo",非常不方便.切换 ...
- 数据结构篇(3)ts 实现双向链表
如今终于到了双向链表了,此前在Node结构中的prev指针终于派上了用场.由于双向链表多了一个前向指针,所以有些操作和单向链表比较起来反而更加的简单. class DbList extends Cir ...
- 【CSAPP】Data Lab实验笔记
前天讲到要刚CSAPP,这一刚就是两天半.CSAPP果然够爽,自带完整的说明文档,评判程序,辅助程序.样例直接百万组走起,管饱! datalab讲的是整数和浮点数怎么用二进制表示的,考验的是用基本只用 ...