目录
1.3 PECS原则(Producer Extends Consumer Super)
一、通配符 (? Wildcards)
1、通配符不能用于泛型定义、不能用于New泛型实例。只能用于泛型类的使用:声明变量、方法的参数。
2、? 是万能通配符 ,表示未知类型,类型参数赋予不确定类型、任意类型
3、<? extends T> 表示类型的上限,表示参数化类型的可能是T 或是 T的子类;
4、<? super T> 表示类型上限(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object;
list<?> list1 = new ArrayList<Integer>();//Ok
public void test(List<?> list)//OK
list1 =new ArrayList<?>()//Error
1.1 泛型上限
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{}
public static void testExtendsReadWriteList(List<? extends Apple> fruits){
//以下都属于 Consumer
Apple fruit = fruits.get(0);
//fruits.add(new Apple())//Error
//fruits.add(new Fruit()) //Error
}
public static void testExtendsReadWrite(Pair<? extends Apple> pair) {
//以下都属于 Consumer
//pair.setFirst(new Fruit());//Error
//pair.setFirst(new Apple());//Error
pair.setSecond(null);
Apple first = pair.getFirst();//ok
}
//OK
public List<? extends Apple> testExtendsProducerList(){
List<Apple> apples =new ArrayList<>();
apples.add(new Apple());
return apples;
}
// Producer 端测试
//ok
List<Apple> apples =new ArrayList<>();
apples.add(new Apple());
testExtendsReadWriteList(apples);
//ok
List<RedApple> redapples =new ArrayList<>();
redapples.add(new RedApple());
testExtendsReadWriteList(redapples);
//ok
List<? extends Apple> apples1 = testExtendsProducerList();
//Error 不满足 函数 ? extends Apple
List<? extends Fruit> fruits= new ArrayList<>();
testExtendsReadWriteList(fruits);//Error
List<? extends Apple> apples2= new ArrayList<>();//ok
testExtendsReadWriteList(apples2);//ok
//apples2.add(new Apple())//error 这本身是一个Consumer
//ok
Pair<Apple> fruitPair1 = new Pair<>();
testExtendsReadWrite(fruitPair1);
//ok
Pair<RedApple> redApplePair = new Pair<>();
testExtendsReadWrite(redApplePair);
//Error 不满足 函数 ? extends Apple
Pair<? extends Fruit> fruitPair = new Pair<>();
testExtendsReadWrite(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<? extends Apple> list1 = getAppleList();//getAppleList方法会返回一个Apple的子类的list
另外,由于我们已经保证了List中保存的是Apple类或者他的某一个子类,所以,可以用get方法直接获得值
List<? extends Apple> list1 = new ArrayList<>();
Apple apple= list1.get(0);//读取出来的东西只能存放在Apple 或它的基类里。
Object object = list1.get(0);//读取出来的东西只能存放在Apple 或它的基类里。
Fruit fruit= list1.get(0);//读取出来的东西只能存放在Apple 或它的基类里。
Son son = (Son)list1.get(0);//只能强转
- <? extends T> 表示类型的上限,表示参数化类型的可能是T 或是 T的子类
- 对于声明了 ? extends T的变量赋值,实际类型 必须是T或是 T的子类
List<RedApple> redapples =new ArrayList<>();
List<Fruit> fruits=new ArrayList<>();
List<? extends Apple> apples1 =redapples ;//OK
List<? extends Apple> apples1 =fruits;//Error
- 对于 带有 <? extends T> 约束的 ,只能读取
1.2 泛型下限
//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);
}
List<? super Fruit> fruits1 = new ArrayList<>();
testSuperReadWriteList(fruits1);
//Error 不满足 函数 ? super Apple
List<? super RedApple> apples1 = new ArrayList<>();
testSuperReadWriteList(apples1);
Pair<? super Fruit> fruitPair2 = new Pair<>();
testSuperReadWrite(fruitPair2);
//Error 不满足 函数 ? super Apple
Pair<RedApple> redApplePair2 = new Pair<>();
testSuperReadWrite(redApplePair2);
因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Apple类,那往里存粒度比Apple小的都可以。出于对类型安全的考虑,我们可以加入Apple对象或者其任何子类(如RedApple)对象,但由于编译器并不知道List的内容究竟是Apple的哪个超类,因此不允许加入特定的任何超类(如Fruit)。而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象,因为Object是任何Java类的最终祖先类。但这样的话,元素的类型信息就全部丢失了。
- 对于声明了 ? super T的变量赋值,实际类型 必须是T或是 T的父类
List<RedApple> redapples =new ArrayList<>();
List<Fruit> fruits=new ArrayList<>();
List<? super Apple> apples1 =redapples ;//Error
List<? super Apple> apples1 =fruits;//Ok
- 下限<? super T>不影响往里存,但值只能是 T和T的子类,不能添加T的父类。但往外取只能放在Object对象里
1.3 PECS原则(Producer Extends Consumer Super)
- Producer Extends:如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;也成为Get 原则
- Consumer Super:如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符,也称为Put原则