基本用法可以参考下面这个问题中胖君的回答
Java 泛型 <? super T> 中 super 怎么 理解?与 extends 有何不同?www.zhihu.com有些疑惑的点在这篇文章中再详细解决一下。
- <? extends T>:上界通配符
要点1:实例化时的类只能是定义时类本身或其子类,也就是说T是它的上界。
import java.util.*;
class Food{
String name = "Food";
}
class Fruit extends Food{
String name = "Fruit";
}
class Apple extends Fruit {
String name = "Apple";
}
// 定义时指定T为Fruit
List<? extends Fruit> list;
// 实例化时类只能是T本身或它的子类
list= new ArrayList<Fruit>(); //可以
list= new ArrayList<Apple>(); //可以
list= new ArrayList<Food>(); //报错
要点2:上界通配符add失效(只能add null),可以get
list = new ArrayList<Fruit>();
list.add(new Fruit()); //报错
list.add(null); //可以
list.get(0); //可以
既然不能add,那里面没东西又get什么呢?所以需要在实例化的时候就把东西都放进去:
list = new ArrayList<Fruit>(){{
add(new Fruit()); // Fruit及其子类都可以放进去
add(new Apple()); // Fruit及其子类都可以放进去
}};
要点3:能放进去的对象类型其上界为实例化时指定的T:
List<? extends Fruit> list list= new ArrayList<Apple>(){{
add(new Fruit()); // 实例化时指定的T为Apple,放入Fruit对象会报错
add(new Apple()); // Apple及其子类都可以放进去
}};
所以能放进去的对象不是看定义时T指定的类,而是看实例化时T指定的类。
2. <? superT>:下界通配符
要点1:实例化时的类只能是定义时类本身或其父类,也就是说T是它的下界。
// 定义时指定T为Fruit
List<? super Fruit> list2;
// 实例化时只能是T本身或它的父类
list2 = new ArrayList<Fruit>(); // 可以
list2 = new ArrayList<Food>(); // 可以
list2 = new ArrayList<Apple>(); // 报错
要点2:添加对象时,在实例化时添加和实例化后添加的对象限制不同(特别注意!):
1. 在实例化时添加的对象只有一个限制:上界为实例化时指定的T。来看一个有趣的现象,即使定义时指定的T是Fruit,但实例化时可以用Fruit的邻居类:
class Food{
String name = "Food";
}
// 新定义一个Meat类,和Fruit都继承Food
class Meat extends Food{
String name = "Meat";
}
class Fruit extends Food{
String name = "Fruit";
}
class Apple extends Fruit {
String name = "Apple";
}
List<? super Fruit> list2 = new ArrayList<Food>(){{
add(new Object()); // 报错,上界为Food
add(new Meat()); // 可以,是Food的子类,即使不是Fruit
add(new Food()); // 可以
add(new Fruit()); // 可以,是Food的子类
add(new Apple()); // 可以,是Food的子类
}};
2. 在实例化后添加的对象,其上界是定义时指定的T:
list2.add(new Meat()); // 报错,不是Fruit或其子类
list2.add(new Food()); // 报错,不是Fruit或其子类
list2.add(new Fruit()); // 可以
list2.add(new Apple()); // 可以
要点3:上界通配符get出来的对象默认是Object类型,可以做强制类型转换,可以add
Object object = list2.get(0);
Meat meat = (Meat)list2.get(0); // 如果不是Object,需要强制类型转换
System.out.println(meat.name); // 输出meat