通配符是Java泛型里的一部分,但是在普通业务代码上应用不会很多,其提供一系列关于编译器上的检查,以保证代码的稳定。这里记录一些这两天学习到的关于通配符的知识。
package com.qpm.learn.base;
import java.util.*;
/**
* @ClassName: UnboundedWildcards1
* @Description: TODO Java泛型学习
* @author kangqiang.w 945766863@qq.com
* @date 2017年3月5日 上午9:34:55
*
*/
class Fruit{}
class Apple extends Fruit{}
class Orange extends Fruit{}
public class UnboundedWildcards1 {
static List list1; //编译器报未确定类型的warn
static List<?> list2;
static List<? extends Fruit> list3; //list3 只接受具有Fruit特征的元素
static List<? super Fruit> list4; //列表的处理下界是Fruit
static void assign1(List list) {
list1 = list;
list2 = list;
list3 = list; //类型安全warning
}
static void assign2(List<?> list){
list1 = list;
list2 = list;
// list3 = list; //可能List是符合要求的,但因为编译器始终无法知道其是否正确,所以直接报了编译错误
}
static void assign3(List<? extends Fruit> list) {
list1 = list;
list2 = list;
list3 = list; // 参数类型的限制保证了参数 的正确
// list.add(new Apple());
}
static void assign4(List<Fruit> list){
list1 = list;
list2 = list;
list3 = list; // 参数类型的限制保证了参数 的正确
}
static void assign5(List<? super Apple> list){
list.add(new Apple());
}
public static void main(String[] args) {
assign1(new ArrayList());
assign2(new ArrayList());
assign3(new ArrayList());
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
// assign3(new ArrayList<String>());
List<?> wildList = new ArrayList();
wildList = new ArrayList<String>();
assign1(wildList);
assign2(wildList);
// assign3(wildList);
/*
* static List<? extends Fruit> list3; //list3 只接受具有Fruit特征的元素
* 这里报编译错误?
* 这是一个与常识有所违背的:list3存储的是具有Fruit特征的元素,但是我们不能向List写入继承与Fruit类的Apple对象
* 解:List<? extends Fruit> 意味着:具有任何从Fruit继承的类型的列表。当一个List<Fruit>列表向上转型为List<? extends Fruit>,它将失去向列表传递对象的能力。
*
* 应用,一个List<? extends Fruit>的列表居然不能让我们传递具体对象,有什么用呢?
* 1、作为一种方法的参数来处理。以List<? extends Fruit>为参数,方法可以接受ArrayList<Apple>或ArrayList<Orange>。
* 假如方法参数是List<Fruit>,那么其只能接受ArrayList<Fruit>的参数类型
*
*
*
*/
list3 = new ArrayList<Apple>();
// list3.add(new Apple());
List<Fruit> list4 = new ArrayList<Fruit>();
list4.add(new Apple());
list4.add(new Orange());
List<Apple> list5 = new ArrayList<Apple>();
assign3(list5);
// assign4(list5);
/*
* 超类型通配符
* List<? super Fruit>编译器会反应到这个集合处理的必需是一个Fruit,
* 如:Apple继承于Fruit,这个变量是不会接受ArrayList<Apple>。因为List<? super Fruit>表示,?的类类型在继承体系中必需高于Fruit。Apple是继承于Fruit,明显是低于Fruit
*/
// list4 = new ArrayList<Apple>();
assign5(new ArrayList<Apple>());
assign5(new ArrayList<Fruit>());
/*
* 同上分析:
* List<? extend Fruit>具有任何从Fruit继承的类型的列表。
* 相对于List<? super Fruit>而言,extend关键字表达的意思是:?的类类型在继承体系中必需低于Fruit。所以List<? extend Fruit> l = new ArrayList<Apple>()是合法的。
* 为什么List<? extend Fruit>不能够传递任何Apple、Fruit的对象进去呢?
* 这与泛型的安全有关,因为编译器至此都无法知道List拥有的是什么类型,所以往这样的list放置对象编译器是不允许的(尽管感觉上没有任何问题),但是相反,从这样的集合获取一个Fruit是允许的,
* 因为编译器知道集合里面的对象起码是一个Fruit。
*/
System.out.println("finish");
}
}
参考资料:《Java编程思想》p389-p396