java泛型

简介

在实际开发中会遇到一些类或者方法在处理数据时,数据具有多样性,如果针对每个数据都新建一个类或者方法来处理就显得颇为麻烦。

常见的场景就是list,里面会存String,Integer等等数据。为了解决这个问题,需要将处理的数据类型告诉这些类或者方法,即参数化类型,把类型当成参数,别名叫泛型,意思是泛化类型。

简单使用

告诉list要处理的类型是String
List<String> strList = new ArrayList<>();
strList.add("a");
strList.add("b");
strList.add("c");

由于在声明时传递给list的类型参数是String,所以在使用时可以直接当做String来使用
System.out.println(strList.get(0).length);

下面示例中使用的Person的子类是Teacher,Teacher的子类是Student.

java中的泛型的由来

java中的泛型有个历史遗留问题,java中的泛型一开始的版本中是没有的,1.5之后才加入,为了兼容以前的版本,在设计时jvm中对于泛型做了类型擦除,即不会记住声明时的类型

这边在泛型内虽然写了Person或者Teacher,但是实际编译时都是list,里面的类型是他们的默认上限,即Object
List<Person> pList = new ArrayList<>();
List<Teacher> tList = new ArrayList<>();

为了兼容版本,使用类型擦除带来的问题

由于java的泛型是伪泛型,所以下面那种本该正常的写法却变错了,因为java默认是不支持型变的,即不支持协变和逆变。

协变:Teacher是Student的父类,那么List<Teacher>也是List<Student>的父类

逆变:Teacher是Person的子类,那么List<Teacher>也是List<Person>的子类

协变,会报错
List<Teacher> tList = new ArrayList<Student>();

逆变,会报错
List<Teacher> tList = new ArrayList<Person>();

java支持型变的方式

java提供了?加上extends或者super来解决这个问题,?extends 来支持协变,? super 支持逆变。

协变,不会报错,extends声明上限
List<? extends Teacher> tList = new ArrayList<Student>();

逆变,不会报错,super声明下限
List<? super Teacher> tList = new ArrayList<Person>();

java中型变后带来的问题

虽然使用extends和super能够支持型变了,但是类型擦除的问题还在,即擦除到上限

所以List<? extends Teacher>,类型被擦除成Teacher,也就是他能接受的类型范围是[Teacher,低线]。
List<? super Teacher>的上限还是Object,他接受的类型范围就是[Object,Teacher]

这就带来下面一些问题,首先看协变带来的问题,在此之前提一下多态,java中支持运行时多态,父类的引用可以指向子类的实例

//协变
List<? extends Teacher> tList = new ArrayList<Student>();
/*
 *由于类型擦除,协变后list类型可能是[Teacher,低线]范围的任何一种
 *jvm不知道具体的类型是啥,也就没法插入任何数据。
 */
tList.add(new Student()); //会报错
tList.add(new Teacher()); //会报错

//只有和类型无关的可以,比如
Teacher teacher = tList.get(0); 
//由于擦除到上限,和多态的存在,不用管list里面存的是啥,Teacher一定是他的父类(或者是Teacher本身),所以能够取出来用teacher的引用指向他。

逆变super和协变问题相反,由于super只定义了下限,所以上限还是Object。

/*
 *逆变 
 *现在tList的 ?superTeacher
 *代表它数据类型的范围可能是[Object,Teacher]
 */
List<? super Teacher> tList = new ArrayList<Person>();

/*
 *如果插入数据是[Object,Teacher]范围内的
 *jvm和之前extends插入数据问题一样,是不知道具体哪个类型的。
 *但是如果插入数据是[Teacher,低线]范围内的,包括Teacher。
 *就可以插入了,这还是多态存在的结果,当插入数据在[Teacher,低线]范围内,
 *就不用管list本身是Object到Teacher范围中哪种类型了,因为他必定是插入数据的父类,也就可以插入了。
 */
tList.add(new Student());
tList.add(new Teacher());

//但是被擦除成Object,所以从list中拿出来的都是Object来接收,既然是Object接收,泛型失去了他的原意了,一般就认为他不可以取数据
Object object = tList.get(0);

总结

java协变(? extends XXX)带来的问题是只能读取不能修改
java逆变(? super XXX) 带来的问题是能够修改一些数据,但是不能读取(如果知道类型拿到Object强转也是可以的)。

这就是PECS法则:Producer-Extends、Consumer-Super

extends的只能生产,super的只能查看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值