简单易懂的泛型上下界理解(extends和super),彻底理解泛型上下界

1、结论

add:
假如现在有List<? extends Xclass>, List<? super Xclass>,一个对象如果想要放进去,它的类型必须同时是它们(List<? extends Xclass>和List<? super Xclass>)限定范围内的任何一种类型。
get:
get相对理解比较简单,在原理部分说


1.1 举例

1.1.1 extends(上界)

如果我想把Aclass这个类型的对象放进List<? extends Xclass>,那么Aclass必须是Xclass类型,在这基础上还必须是Xclass的任何子类类型。
比如: 有以下继承关系
在这里插入图片描述

List<? extends Human> eHumans = new ArrayList<>();
Human human = new Human();
Man man = new Man();
# 下面我们执行add操作
eHumans.add(human); # !!! 这个是不对的,会报异常
eHumans.add(man); # !!! 这个是不对的,会报异常
eHumans.add(null) # 这个是对的

man这个对象必须是Human类型(它是),必须是Man类型(它是),还必须是Woman类型(它不是),还必须是LazyMan类型(它不是),而null对象是任何类型,所以能放进去

总结: 除了null对象,任何类型都不可能既是该类型又是其所有子类的类型,所以上界只能add(null);

1.1.1 super(下界)

如果我想把Aclass这个类型的对象放进List<? super Xclass>,那么Aclass必须是Xclass类型,在这基础上还必须是Xclass的任何父类的类型。

List<? super LazyMan> sLazyMans = new ArrayList<>();
LazyMan lazyMan = new LazyMan();
Man man = new Man();
Human human = new HuMan()

# 下面我们执行add操作
sLazyMans.add(lazyMan ) # 这个是对的
sLazyMans.add(man ) # !!! 这个是不对的,会报异常
sLazyMans.add(human ) # !!! 这个是不对的,会报异常
eHumans.add(null); # 这个是对的

lazyMan对象是LazyMan类型,也是Man、HuMan、Object类型,所以能放进去man和human对象首先就不是LazyMan类型,所以不能放进去,而null对象是任何类型,所以能放进去;

总结: 除了null对象和该类型,任何类型都不可能既是该类型及其所有父类的类型,所以上界只能add(null)和add(该类型);

2、原理

要理解原理还需要提到引用的泛型、对象的泛型、类型推断

2.1 引用的泛型、对象的泛型、类型推断

很早之前代码中使用泛型需要这样写

List<LazyMan> sLazyMans = new ArrayList<LazyMan>();

但是后来只需要这样写

List<LazyMan> sLazyMans = new ArrayList<>();

这是因为java引入了类型推断,意思编译器能推断出你new ArrayList<>的尖括号里想要的是LazyMan,所以我们就能省略不写了。
所以我称new ArrayList<>的尖括号里面的泛型为对象泛型,List<>中的泛型为引用泛型

对象泛型中是不能使用上下界的

new ArrayList<? extends LazyMan>(); #编译报错
new ArrayList<? super LazyMan>(); #编译报错
new ArrayList<LazyMan>(); # 正确

引用泛型中是可以使用上下界的

List<? extends LazyMan> list1; #正确
List<? super LazyMan> list2; #正确
List<LazyMan> list3; #正确

2.2 代码解释原因

先总结一下上面的: 对象泛型只能接收具体的类型,引用泛型才能接收通配符(即上下界),List<? extends LazyMan>或List<? super LazyMan>的含义是这个引用能指向这个范围内的所有指定了具体类型的List对象,而不是表示当前引用指向的对象能装这个范围内的对象

2.2.1 extends

即真正的用法是下面这样:

List<? extends Human> list1;
List<Man> mans = new ArrayList<>();
List<Woman> womans = new ArrayList<>();
List<LazyMan> LazyMans = new ArrayList<>();

list1 = mans ;
list1 = womans ;
list1 = LazyMans ;

如果代码中list1表示的是能装List<? extends Human>这个范围内的对象的话就会出现什么情况呢

# 当前list1 指向的对象是ArrayList<Man>
list1 = mans;

 # 如果意思指的是允许装Human的子类,那下面两条等价语句很容易就能看出错误
list1.add(new Woman());
mans.add(new Woman());
#我们可能看不出list1.add(new Woman())的错误,但我们能一眼看出mans.add(new Woman())是错误的,
#因为mans指向的是ArrayList<Man>对象,代表里面是Man类型的对象,但我们却放一个Woman进去?
#WomanMan吗?显而易见不是

2.2.2 super

super的用法就是这样:

List<? super LazyMan> list2;
List<Man> mans = new ArrayList<>();
List<Strong> strongs= new ArrayList<>();
List<Human> humans = new ArrayList<>();
List<Object> objects = new ArrayList<>();

list2 = mans ;
list2 = strongs;
list2 = humans ;
list2 = objects ;

如果代码中list2表示的是能装List<? super Human>这个范围内的对象的话就会出现什么情况呢

# 当前list2 指向的对象是ArrayList<Strong>
list2 = strongs;

 # 如果意思指的是允许装LazyMan的父类,那下面两条等价语句很容易就能看出错误
list2.add(new Human());
strongs.add(new Human());
#我们可能看不出list2.add(new Human());的错误,但我们能一眼看出strongs.add(new Human());是错误的,
#因为strongs指向的是ArrayList<Strong>对象,代表里面是Strong类型的对象,但我们却放一个Human进去?
#StrongHuman吗?显而易见不是

#而下面这样是正确的, 因为LazyManStrong类型
list2.add(new LazyMan());
strongs.add(new LazyMan());

2.3 get方法

很好理解

List<? extends Human> list1; list1指向的肯定对象泛型是Human的子类的List对象,即可以是 list1 = new ArrayList(); 可以是list1 = new ArrayLIst(); 集合里面的对象一定是Human类型,所以get方法的返回就是Human类型。

List<? super LazyMan> list1; 即可以是 list1 = new ArrayList(); 可以是list1 = new ArrayLIst(); 可以是list1 = new ArrayLIst(); 可以是list1 = new ArrayLIst(); 我们只能确定集合里面的对象一定是Object类型,因为所有对象都是Object类型嘛,所以get方法的返回就是Object类型。

最后总结:

List<? extends LazyMan>或List<? super LazyMan>的含义是这个引用能指向这个范围内的所有指定了具体类型的List对象,而不是表示当前引用指向的对象能装这个范围内的对象。如果想要通过引用变量点add方法的方式放入一个对象进去,这个对象必须同时是通配符限定范围内的任何一种类型

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值