泛型<? extends T>和<? super T>

前段时间看《Java编程思想》泛型时对 <? extends T>与<? super T>很懵逼,接着看到泛型与集合的更蒙蔽,随后又翻开《码出高效》时,对这些知识点才恍然大悟,发篇博客记录下

List、List<Object>、List<?> 的三者的区别以及 <? extends T>与<? super T> 的区别

List、List<Object>、List<?>

  • List :完全没有类型限制和赋值限定。
  • List<Object> :看似用法与List一样,但是在接受其他泛型赋值时会出现编译错误。
  • List<?>:是一个泛型,在没有赋值前,表示可以接受任何类型的集合赋值,但赋值之后不能往里面随便添加元素,但可以remove和clear,并非immutable(不可变)集合。List<?>一般作为参数来接收外部集合,或者返回一个具体元素类型的集合,也称为通配符集合

代码验证:(感觉用博客的页面插入代码效果不好,在此处贴了图片,代码在文章末尾补上)
在这里插入图片描述
<? extends T>与<? super T>

List 最大的问题是只能放置一种类型,如果随意转变类型的话,就是破窗理论,泛型就失去了意义。为了放置多种受泛型约束的类型,出现了 <? extends T>与<? super T> 两种语法。简单来说, <? extends T> 是Get First,适用于,消费集合元素的场景;<? super T>是Put First,适用于,生产集合元素为主的场景。

  • <? extends T> :可以赋值给任意T及T的子类集合,上界为T,取出来的类型带有泛型限制,向上强制转型为T。null 可以表示任何类型,所以null除外,任何元素都不得添加进<? extends T>集合内。
  • <? super T> : 可以复制T及任何T的父类集合,下界为T。再生活中,投票选举类似于<? super T>的操作。选举投票时,你只能往里投票,取数据时,根本不知道时是谁的票,相当于泛型丢失

<? extends T>的场景是put功能受限,而<? super T>的场景是get功能受限

代码示例如下(以加菲猫、猫、动物为例,说明extends和super的详细语法差异0):
在这里插入图片描述
在这里插入图片描述
在代码第二段中的23行编译时报错:
Type mismatch: cannot convert from List to List<? extends Cat>
因为List赋值给List时会编译出错,因为能赋值给<? extends Cat>的类型,只有Cat和它的子类。因为<? extends Cat>泛型信息表示的是,此笼子(容器)内的全部都是猫,而List笼子(容器)内可能住着毒蛇,鳄鱼、豹猫(猫的天敌)、大型猛禽等动物,不能把它们放同一个笼子(容器)里。 26,27行中,把List赋值给 <? extends T>与<? super T> 都是可以的。

在第三段代码的36、36、37行均编译出错:
The method add(capture#1-of ? extends Cat) in the type List<capture#1-of ? extends Cat> is not applicable for the arguments (Animal)
无法进行add操作,这是因为除null外,任何元素都不能添加进<? extends T>集合中。但<?super Cat>可以往里面添加元素,但只能添加Cat自身即其子类对象,如上面代码中的41、42行。因为猫的笼子中只能关猫,不能关其他动物,如代码中的40行。

在上面代码中的第四段中,所有的 List<? super T>集合 都能执行get方法返回元素,但是泛型丢失,只能返回object对象,如上面代码中的46、47、48行。List<? extends T>集合 可以返回带类型的元素,但只能返回Cat自身及其父类对象,因为子类对象被擦除了,如代码中的50到54行。

附:
第一张图片中的代码

import java.util.ArrayList;
import java.util.List;
public class TestArrayList {
	public static void main(String[] args) {
	
		//第一段:泛型出现之前集合定义方式
		List a1 =new ArrayList();
		a1.add(new Object());
		a1.add(new Integer(10));
		a1.add(new String("string"));
		
		//第二段:把a1引用赋值给a2,(a2与a1的区别是增加了泛型限制)
		List<Object> a2 =a1;
		a2.add(new Object());
		a2.add(new Integer(20));
		a2.add(new String("string2"));
		a2.add(25);
		//List<Object> 接受其他泛型赋值时,会报异常(因为在下面例子中List<Integer>不能转为List<Object>)
		List<Integer> aint = new ArrayList<Integer>();
		List<Object> a22 =aint;//Type mismatch: cannot convert from List<Integer> to List<Object>
		
		//第三段:把a1引用赋值给a3,(a3与a1的区别是增加了泛型<Integer>)
		List<Integer> a3 = a1; //此时如果遍历a3则会报类型转换异常ClassCastException
		a3.add(new Integer(20));
		//下面两行编译出错,不允许增加非Integer类型进入集合
		a3.add(new Object());//The method add(Integer) in the type List<Integer> is not applicable for the arguments (Object)
		a3.add(new String("string2"));
		
		//第四段:把a1引用赋值给a4,a4与a1的区别是增加了通配符
		List<?> a4 = a1;
		//允许删除和清除元素
		a4.remove(0);
		a4.clear();
		//编译错误,不允许添加任何元素
		a4.add(new Object());//The method add(capture#3-of ?) in the type List<capture#3-of ?> is not applicable for the arguments (Object)
		a4.add(new Integer(20));
		a4.add(new String("string2"));
	}
}

第二张图片中的代码

import java.util.ArrayList;
import java.util.List;
class Animal{}
class Cat extends Animal{}
class Garfield extends Cat{}

//用动物,猫,加菲猫的继承关系说明extends与super在集合中的意义
public class AnimalCatGarfield {
	public static void main(String[] args) {
		//第一段:声明第三个依次继承的集合:Object>动物>>加菲猫  三个泛型集合可以理解为三个不同的笼子
		List<Animal> animal = new ArrayList<Animal>();        //动物
		List<Cat> cat = new ArrayList<Cat>();				  //猫
		List<Garfield> garfield = new ArrayList<Garfield>();  //加菲猫
		
		animal.add(new Animal());
		cat.add(new Cat());
		garfield.add(new Garfield());
		
		//第二段:测试赋值操作  以Cat为核心,因为它既有子类又有父类
		//下行编译出错。只能赋值Cat或Cat子类集合
		List<? extends Cat> extendsCatFromAnimal = animal;
		List<? super Cat> superCatFromAnimal = animal;
		
		List<? extends Cat> extendsCatFromCat = cat;
		List<? super Cat> superCatFromCat = cat;
		
		List<? extends Cat> extendsCatFromGarfield = garfield;
		//下行编译出错。只能赋值Cat或着Cat父类集合
		List<? super Cat> superCatFromGarfield = garfield;
		
		//第三段:测试add方法
		//下面三行中所有的<? extends T>都无法进行add操作,编译出错
		extendsCatFromCat.add(new Animal());
		extendsCatFromCat.add(new Cat());
		extendsCatFromCat.add(new Garfield());
		
		//下行编译出错。只能添加Cat或者Cat的子类集合。
		superCatFromCat.add(new Animal());
		superCatFromCat.add(new Cat());
		superCatFromCat.add(new Garfield());
		
		//第四段:测试get方法
		//所有的super操作能够返回元素,但是泛型丢失,只能返回object对象
		Object object1 = superCatFromCat.get(0);
		Animal object = superCatFromCat.get(0);//Type mismatch: cannot convert from capture#8-of ? super Cat to Animal
		Cat object3 = superCatFromCat.get(0);//
		//以下extends操作能够返回元素
		Animal catExtends3 = extendsCatFromCat.get(0);
		Object catExtends2 = extendsCatFromCat.get(0);
		Cat catExtends1 = extendsCatFromCat.get(0);
		//下行编译错误。虽然Cat集合从Garfield赋值而来,但类型擦除后,是不知道的
		Garfield cat2 = extendsCatFromGarfield.get(0);
	}
}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java的中,`<? extends T>`和`<? super T>`是用来限定通配符(Wildcard)的上界和下界。 1. `<? extends T>`:表示通配符的上界是T或T的子类。使用`<? extends T>`可以使接受T或T的子类作为参数,但不能用于写入对象。 ```java public void processList(List<? extends Number> list) { for (Number num : list) { System.out.println(num); } } List<Integer> integers = new ArrayList<>(); integers.add(1); integers.add(2); processList(integers); // 可以传递List<Integer>或List<Number>,但不能传递List<Object> ``` 在上述示例中,`processList()`方法接受一个`List<? extends Number>`类的参数,这意味着可以传递`List<Integer>`或`List<Number>`作为参数。在方法内部,我们可以从list中读取Number类的元素,因为Number是Integer的父类。 2. `<? super T>`:表示通配符的下界是T或T的父类。使用`<? super T>`可以使接受T或T的父类作为参数,并且可以用于写入对象。 ```java public void addToList(List<? super Integer> list) { list.add(1); list.add(2); } List<Number> numbers = new ArrayList<>(); numbers.add(0.5); addToList(numbers); // 可以传递List<Integer>或List<Object>,但不能传递List<Number> System.out.println(numbers); // 输出:[0.5, 1, 2] ``` 在上述示例中,`addToList()`方法接受一个`List<? super Integer>`类的参数,这意味着可以传递`List<Integer>`或`List<Object>`作为参数。在方法内部,我们可以向list中添加Integer类的元素,因为Integer是Number的子类。 总结: - `<? extends T>`用于限定的上界,可以读取对象,但不能写入; - `<? super T>`用于限定的下界,可以写入对象,但读取时需要进行类转换。 使用通配符的目的是为了增加的灵活性,在不确定具体类的情况下,能够处理更广的数据类

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值