一、超类型通配符(supertype wildcards)
可声明通配符是某特定类的任何父类界定,如< ? super MyClass>,或者< ? super T> ,不过不存在< T super MyClass>,这样可以安全地传递一个类型对象到泛型类型中。有了superType wildcards ,就可以向Collection写入了。
public class SuperTypeContravariant {
void writeTo(List<? super Apple> apples) {
apples.add(new Apple());
Apple apple = (Apple) apples.get(0); // 由于apples 中加入的肯定都是 Apple 或其子类对象,所以向上强转为Apple是可行的
System.out.println(apple);
Object object = apples.get(0); // 用Object接收
System.out.println(object);
}
public static void main(String[] args) {
new SuperTypeContravariant().writeTo(new ArrayList<>(10));
}
class Apple{}
说到这里,我们要注意一下怎么样去感性地认识带边界的通配符:
比如: List< ? extends MyClass> list1,即 MyClass是上界:
这里要把 **< ? extends MyClass> 当成一个整体,说明这个List类型是MyClass或其子类类型中确定的某一种,**由于并不知道到底是哪个确定的类型,所以不能add 元素,即使是Object也不行。**不过可以肯定从list1中get到的元素都是MyClass或其子类类型,所以可以从list1中get。
再看:List< ? super MyClass> list2,即MyClass是下界:
这里也把**< ? super MyClass>当成整体,说明这个类型是MyClass或其父类类型中的一种**,无论是确定的哪一种,我们add MyClass或其子类类型的实例都是可以的。而如果add MyClass的父类实例,那就破坏了静态类型安全了。get 元素时,由于并不确定具体的类型,所以只能用Object 去接收。但是由于list2中元素都是Myclass类型或其子类,所以get到元素后向上强转为MyClass也是可行的。
二、superType wildCards的场景
超类通配符放松了能向方法传递的参数上所做的限制。
public class GenericWriting {
static List<Apple> apples = new ArrayList<>();
static List<Fruit> fruits = new ArrayList<>();
static <T> void writeExact(List<T> list, T item) {
list.add(item);
}
static void f1() {
writeExact(apples, new Apple());
writeExact(fruits, new Apple());
// 在旧版本的JDK中,这句报 incompatible types 的异常,所以在
// writeWithWildCard 中将参数类型变为超类通配符< ? super T>,
// 这样就可以将T 或其 子类类型的对象作为参数传递给List
// 值得注意的是:在JDK8+版本中,编译器变聪明了,这句代码是正常的,所以就这个例子来说,有点鸡肋
}
static <T> void writeWithWildCard(List<? super T> list, T item) {
list.add(item);
}
static void f2() {
writeWithWildCard(apples, new Apple());
writeWithWildCard(fruits, new Apple());
}
public static void main(String[] args) {
f1();
f2();
}
}
这个Demo中最关键的要注意到:
static < T> void writeWithWildCard(List< ? super T> list, T item) {} 的入参已经变成了
List< ? super T> list ,**这个List将持有T或其子类的某个具体类型。**这样就能将一个T或其子类类型对象作为参数传递到List的方法。
三、协变和通配符
public class GenericReading {
static List<Apple> apples = Arrays.asList(new Apple());
static List<Fruit> fruits = Arrays.asList(new Fruit());
static <T> T readExact(List<T> list) { // 精确类型
return list.get(0);
}
private static void f1() {
Apple apple = readExact(apples);
Fruit fruit = readExact(fruits);
fruit = readExact(apples); // Apple upcast to Fruit
}
/*泛型没有内建协变*/
static class Reader<T> {
T readExact(List<T> ts) {
return ts.get(0);
}
}
static void f2() {
Reader<Fruit> fruitReader = new Reader<>();
Fruit fruit = fruitReader.readExact(fruits);
// fruitReader.readExact(apples); // 不兼容的类型: List<Apple>无法转换为 List<Fruit>
}
/*泛型边界(协变类型)*/
static class CovariantReader<T> {
//从 List<? extends T> list 得到的 元素类型必定是T或其子类类型
T readCovariant(List<? extends T> list) {
return list.get(0);
}
}
static void f3() {
CovariantReader<Fruit> covariantReader = new CovariantReader<>();
Fruit f1 = covariantReader.readCovariant(apples); // in fact ,it is an apple!
Fruit f2 = covariantReader.readCovariant(fruits); // it is surely a fruit!
}
public static void main(String[] args) {
f1();f2();f3();
}
}
四、测试的demo
public class GenericTest {
/**
* 方法 m1 : 具有一个调用了 Generic1 方法的 **逆变参数**:::向一个泛型类型中写(传递给一个方法),所以
* 用 限下边界。或者说,这是一个 T 消费者
* @param arg Generic1类的实例,类型为 T或其父类类型中的确定某一种
*/
<T> void m1(Generic1<? super T> arg, T t) {
arg.take(t);
}
/**
* 方法m2 : 具有一个调用 Generic2 方法的 **协变参数** ::: 从一个泛型类型中读 ( 从一个方法中返回) ,所以
* 用 限上边界。或者说,这是一个 T 生产者
* @param arg Generic2类的实例, 类型为T或其子类类型中的确定某一种
*/
<T> void m2(Generic2<? extends T> arg) {
T give = arg.give();
}
public static void main(String[] args) {
GenericTest genericTest = new GenericTest();
// 报错: cannot be applied
// 原因:no instances of type variables exist so that pet conform to cat
// inference variable T has incompatible types:
// lower bounds: pet
// upper bounds:Object ,Cat
// 意思是:按下面这行代码,T 的类型推断就会出错: T 的下界是pet ,上界又是 Object cat
genericTest.m1(new Generic1<Cat>(), new Pet());
// 正常
genericTest.m1(new Generic1<Pet>(), new Cat());
genericTest.m2(new Generic2<>());
}
}
/*Generic1 只有一个方法,接收一个T类型的参数*/
class Generic1<T>{
T t;
void take(T t) {
this.t = t;
}
}
/*Generic2 只有一个方法,返回一个T类型的参数*/
class Generic2<T>{
T t;
T give() {
return t;
}
}
class Pet{}
class Cat extends Pet{}