《零基础学 Java》 FAQ 之 16- 范型引用的通配符再解

用户头像
臧萌
关注
发布于: 2020 年 05 月 26 日
《零基础学 Java》 FAQ 之 16-范型引用的通配符再解

很多同学对这个范型里的通配符很迷。这里就再讲一下。



类设计:Child extends Parent extends GrandParent extends Human。Human类里有一个name属性和getName,setName方法。



下面一个类,就让大家理解这个范型的通配符是干嘛的。都在代码里了。



package com.paypal.edm.dsl.udf.test;
import java.util.ArrayList;
import java.util.List;
/**
* @author mzang
*/
public class Test {
// --------- ? extends T 例子:从范型对象里读-------------------------------------
/**
* 这个方法要遍历List<Human>里的元素,要求每个元素都要求是Human,或者其子类
* @param humans
*/
public static void showNameAndAddMoreNotWildGeneric(List<Human> humans){
humans.forEach(Human::getName);
// 可以
humans.add(new Human());
}
/**
* 这个方法要遍历List<? extends Human>里的元素,也要求每个元素都要求是Human,或者其子类
* @param humans
*/
public static void showNameWildGeneric(List<? extends Human> humans){
humans.forEach(Human::getName);
// 不行
// humans.add(new Human());
// humans.add(new Child());
}
// 问题在于,参数不同,showNameNotWildGeneric的参数使用了范型匹配符。
// 接下来我们来看看这两个方法的参数有什么不一样。
public static void testConsume() {
// 创建一个正常使用范型的List,要求里面的元素都是Parent或者其子类
List<Parent> parentList = new ArrayList<>();
// 用List<Parent>类型的parentList去调用showNameAndAddMoreNotWildGeneric,会出错。为什么呢?
// 因为showNameAndAddMoreNotWildGeneric里,最后还向List里增加了一个Human元素
// 但是这个参数的本体,是List<Parent>,parentList也指向这个对象。
// 所以方法里的humans.add(new Human());,就等价于是parentList.add(new Human());
// 类型不符合,肯定不允许呀
// showNameAndAddMoreNotWildGeneric(parentList);
// 而showNameWildGeneric,则通过范型wild皮配符,限制了这个引用写入数据的能力
// List<? extends Human> humans的意思是,humans指向的可能是任意一个集成Human的List
// 可以是List<Parent>,也可以是List<Child>,或者List<GrandParent>,或者任意继承Human的类的List
// 这种情况下,向里面写什么,都有错的可能,因为你不知道具体是个什么范型的List。
showNameWildGeneric(parentList);
}
// --------- ? extends T 例子:向范型对象里写-------------------------------------
// 下面两个方法:addMoreHumanWildGeneric 和 addMoreHumanNotWildGeneric,都可以写入相同类型的实例
// 不同在于他们可以接受的参数类型。有范型wild的,更宽泛一点,但是不能从里面读取有类型信息的数据。
public static void addMoreHumanWildGeneric(List<? super Parent> humans){
// humans.add(new Human());
// humans.add(new GrandParent());
humans.add(new Child());
humans.add(new Parent());
// 从List<? super Parent> humans里读取的对象,类型是未知的
// humans.forEach(Human::getName);
}
public static void addMoreHumanNotWildGeneric(List<Parent> humans){
// humans.add(new Human());
// humans.add(new GrandParent());
humans.add(new Child());
humans.add(new Parent());
// 但是从List<Parent> humans里读取的对象,类型是Human或者其子类
humans.forEach(Human::getName);
}
public static void testProduce() {
// 用List<Parent> parentList做参数,两个方法都可以。
List<Parent> parentList = new ArrayList<>();
addMoreHumanWildGeneric(parentList);
addMoreHumanNotWildGeneric(parentList);
// 用List<GrandParent> childList做参数,addMoreHumanNotWildGeneric就不可以了。
// 因为addMoreHumanNotWildGeneric要求参数必须是List<Parent>,不考虑继承关系。
// 而addMoreHumanWildGeneric里的参数List<? super Parent> humans,则支持继承关系的兼容即可复制
// 但是却不支持读取,因为里面的元素可以是Parent的任意父类。
List<GrandParent> childList = new ArrayList<>();
addMoreHumanWildGeneric(childList);
// addMoreHumanNotWildGeneric(childList);
}
}



范型的通配符,其实遇到的问题和数组是一样的。只是数组没有类型通配符这么个功能,所以更简单一点。






这篇文章来自极客时间推出的《零基础学Java》中的FAQ。除了在每节视频课下方回答大家的问题之外,针对大家提出的优质问题或者普遍问题,如果需要更大篇幅的文章解答,则会在FAQ中以文章的方式给出回答。带你零基础入门,夯实Java,课程地址:https://time.geekbang.org/course/intro/181



发布于: 2020 年 05 月 26 日 阅读数: 52
用户头像

臧萌

关注

一线程序员,偶尔写写字 2017.10.20 加入

《零基础学 Java》,《职场求生攻略》 视频课作者 《Java入门1·2·3》作者

评论 (1 条评论)

发布
用户头像
Java的相关文章可以在这个帖子里盖楼参与现金大奖活动https://xie.infoq.cn/article/d03635d2965e0af9244b736e5
2020 年 06 月 05 日 10:03
回复
没有更多了
《零基础学 Java》 FAQ 之 16-范型引用的通配符再解