目录
1.3 PECS原则(Producer Extends Consumer Super)
一、通配符 (? Wildcards)
1、通配符不能用于泛型定义、不能用于New泛型实例。只能用于泛型类的使用:泛型类变量声明、方法参数声明。
2、? 是万能通配符 ,表示未知类型,类型参数赋予不确定类型、任意类型
3、<? extends T> 表示类型的上限,表示参数化类型的可能是T 或是 T的子类;
4、<? super T> 表示类型上限(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object;
5、<? extends T>,<? super T> 约束的泛型类内部的类型变量
声明泛型类变量时 使用 <? extends T> ,对泛型类内的类型变量 只能读,不能写
声明泛型类变量时 使用 <? super T> ,对泛型类内的类型变量 可以写(只能写T或T的子类),读只能读Object
list<?> list1 = new ArrayList<Integer>();//Ok
public void test(List<?> list)//OK
list1 =new ArrayList<?>()//Error
泛型类定义
public class Pair<T>{
private T first;
private T second;
public Pair() { first = null ; second = null ; }
public Pair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
public class Fruit{ }
public class Apple extends Fruit{}
public class Pear extends Fruit{}
public class RedApple extends Apple{}
1.1 泛型上限 <? extends T>
- 对于声明了 ? extends T的变量赋值,实际类型 必须是T或是 T的子类
List<RedApple> redapples =new ArrayList<>();
List<Fruit> fruits=new ArrayList<>();
List<? extends Apple> extendsAppleList=redapples ;//OK
List<? extends Apple> extendsAppleList=fruits;//Error
- 声明泛型类变量时 使用 <? extends T> ,对泛型类内的约束的类型变量 只能读,不能写。
List<? extends Apple> extendsAppleList;
Pair<? extends Apple> extendsApplePair;
对于extendsAppleList,约束的类型变量就是List的元素,即 不能执行add 方法
对于extendsApplePair,约束的类型变量就是first,second两个类成员,即不能执行 setFirst,setSecond 方法
public static void testExtendsReadWriteList(List<? extends Apple> fruits){
//可以读
Apple fruit = fruits.get(0);
// 能不能对List<? extends Apple>声明的变量fruits 的内部成员写:
//fruits.add(new Apple())//Error
//fruits.add(new Fruit()) //Error
}
public static void testExtendsReadWrite(Pair<? extends Apple> pair) {
//不能对first second 两个成员变量写
//pair.setFirst(new Fruit());//Error
//pair.setFirst(new Apple());//Error
//可以读
Apple first = pair.getFirst();//ok
}
//声明泛型类变量的赋值
List<? extends Apple> extendsAppleList;
List<RedApple> redAppleList = new ArrayList<>();
List<Apple> appleList = new ArrayList<>();
List<Fruit> fruitList = new ArrayList<>();
//ok
extendsAppleList=appleList;
//ok
extendsAppleList=redAppleList;
//Error 实际类型Fruit 不满足 ? extends Apple
extendsAppleList= fruitList;
Pair<? extends Apple> extendsApplePair;
Pair<Fruit> fruitPair = new Pair<>();
Pair<Apple> applePair = new Pair<>();
Pair<RedApple> redApplePair = new Pair<>();
//ok
extendsApplePair= redApplePair;
//ok
extendsApplePair=applePair;
//Error 实际类型Fruit不满足 ? extends Apple
extendsApplePair=fruitPair;
setFirst / add 的调用有一个类型错误,编译器只知道需要某个 Apple的子类型,但不知道
具体是什么类型。它拒绝传递任何特定的类型。毕竟 任何类型 和 ?都不匹配。
使用 getFirst /get 就不存在这个问题: 将 getFirst 的返回值赋给一个 Apple(或其父类)的引用完全合法原因是编译器只知道容器内是Apple或者它的派生类,但具体是什么类型不知道。可能是Apple?可能是RedApple?编译器在看到后面用Apple赋值以后,集合里并没有限定参数类型是“Apple“。而是标上一个占位符:CAP#1,来表示捕获一个Apple或Apple的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入Apple或者RedApple或Fruit编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。
List<? extends Apple> list不能进行add,但是,这种形式还是很有用的,虽然不能使用add方法,但是可以在初始化的时候指定不同的类型。比如:
List<RedApple> redAppleList = new ArrayList<>()
List<? extends Apple> extendsAppleList= redAppleList;
另外,由于我们已经保证了List中保存的是Apple类或者他的某一个子类,所以,可以用get方法直接获得值
Apple apple= extendsAppleList.get(0);//读取出来的东西只能存放在Apple 或它的基类里。
Object object = extendsAppleList.get(0);//读取出来的东西只能存放在Apple 或它的基类里。
Fruit fruit= extendsAppleList.get(0);//读取出来的东西只能存放在Apple 或它的基类里。
RedAppleson = (RedApple)extendsAppleList.get(0);//只能强转
1.2 泛型下限 <? super T>
- 对于声明了 ? super T的变量赋值,实际类型 必须是T或是 T的父类
List<RedApple> redapples =new ArrayList<>();
List<Fruit> fruits=new ArrayList<>();
List<? super Apple> superAppleList=redapples ;//Error
List<? super Apple> superAppleList=fruits;//Ok
- 声明泛型类变量时 使用 <? super T>不影响 泛型类内的约束的类型变量的写,但值只能是 T和T的子类,不能添加T的父类。往外取只能放在Object对象里
List<? superApple> superAppleList;
Pair<? superApple> superApplePair;
对于superAppleList,约束的类型变量就是List的元素,可以执行add 方法
对于superApplePair,约束的类型变量就是first,second两个类成员,可以执行 setFirst,setSecond 方法
superApplePair.setFirst(new Apple());//OK
superApplePair.setFirst(new RedApple());//OK
//superApplePair.setFirst(new Fruit());//Error
Object first = pair.getFirst();//OL
//super只能添加Apple和Apple的子类,不能添加Apple的父类,读取出来的东西只能存放在Object类里
public void testSuperReadWrite(Pair<? super Apple> pair){
pair.setFirst(new Apple());
pair.setFirst(new RedApple());
//pair.setFirst(new Fruit());//Error
Object first = pair.getFirst();
}
//super只能添加Apple和Apple的子类,不能添加Apple的父类,读取出来的东西只能存放在Object类里
public void testSuperReadWriteList(List<? super Apple> list){
list.add(new Apple());
list.add(new RedApple());
//list.add(new Fruit());//Erro
Object object = list.get(0);
}
//只能赋值是Apple及父类的的List
List<? super Apple> superAppleList;
List<RedApple> redAppleList = new ArrayList<>();
List<Apple> appleList = new ArrayList<>();
List<Fruit> fruitList = new ArrayList<>();
//ok
superAppleList= fruitList;
//ok
superAppleList=appleList;
//Error 实际类型RedApple不满足 ? super Apple
//superAppleList=redAppleList;
//只能赋值是Apple及父类的Pair
Pair<? super Apple> superApplePair;
Pair<Fruit> fruitPair = new Pair<>();
Pair<Apple> applePair = new Pair<>();
Pair<RedApple> redApplePair = new Pair<>();
//ok
superApplePair=fruitPair;
//ok
superApplePair=applePair;
//Error 实际类型RedApple不满足 ? super Apple
superApplePair= redApplePair;
因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Apple类,那往里存粒度比Apple小的都可以。出于对类型安全的考虑,我们可以加入Apple对象或者其任何子类(如RedApple)对象,但由于编译器并不知道List的内容究竟是Apple的哪个超类,因此不允许加入特定的任何超类(如Fruit)。而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象,因为Object是任何Java类的最终祖先类。但这样的话,元素的类型信息就全部丢失了
1.3 PECS原则(Producer Extends Consumer Super)
- Producer Extends:如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;也成为Get 原则
- Consumer Super:如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符,也称为Put原则

本文详细介绍了Java中泛型通配符的使用,包括万能通配符?、类型上限? extends T和下限? super T的应用,以及PECS原则在生产者消费者场景中的实践。通过实例展示如何正确地在方法参数和列表操作中运用这些概念。

1609

被折叠的 条评论
为什么被折叠?



