###泛型通配符的由来
看下面的一段代码
B[] b = new B[5];
A[] a = b;
try {
a[0] = new A();
}catch (Exception p ){
}
class A{
}
class B extends A{
}
上面的代码中在编译的时候没有问题但是在运行的时候会报错。这是因为在编译阶段该数组是A类型的,在运行阶段jvm才转化为B类型,所以我们在编译的时候将a[0]赋值为A的一个对象的时候不会报错,运行的时候才报错。
再看下面的一个代码
List<A> list = ArrayList<B>();//报错
####什么是泛型通配符?
泛型在我们开发中经常使用,泛型通配符在我们使用泛型的时候也是十分重要的,泛型通配符包括? , ? extends T, ? super T三种。注:泛型仅仅存在编译时期,在转化的为字节码文件的时候会进行泛型擦除。
#####无界通配符
?为无界通配符,表示具体不知道类型是什么,可以简单理解为List(但是他们不相同),而且这样定义后不能往里面添加东西。
List<?> list = new ArrayList<Integer>();
list.add(1);//报错
#####有界通配符
? extends T上界通配符,表示传递的类型必须是T以及T的子类。
public static void main(String[] args){
List<A> list1 = new ArrayList<A>();
List<B> list2 = new ArrayList<B>();
List<C> list3 = new ArrayList<C>();
show(list1);
show(list2);
show(list3);//报错
}
public static void show(List<? extends A> list){
}
class A{
}
class B extends A{
}
class C{
}
? super T下界通配符,表示传递的类型必须是T以及T的父类。
public static void main(String[] args){
List<A> list1 = new ArrayList<A>();
List<B> list2 = new ArrayList<B>();
List<C> list3 = new ArrayList<C>();
show(list1);
show(list2);
show(list3);//报错
}
public static void show(List<? super B> list){
}
class A{
}
class B extends A{
}
class C{
}
#####副作用
public static void main(String[] args){
List<? extends A> list1 = new ArrayList<B>();
List<? super B> list2 = new ArrayList<A>();
list1.add(new B());//报错
A a = list1.get(0);
B b = list1.get(0);//报错
list2.add(new B());
list2.add(new A());//报错
A a1 = list2.get(0);//报错
Object o = list2.get(0);
}
class A{
}
class B extends A{
}
- 使用extends和super会带来一些副作用。
- list1中,由于我们在编译期间对于list1只知道他是A的子类容器,但是无法确定是什么子类的容器,所以索性直接不能添加,但是容器里面的存储东西都可以向上转型为A类或者A的父类,所以可以取出存在A和A的父类中。
- list2中,当我们get的时候,只能将其放在Object对象中(因为他是所有对象的父类),因为无法确定我们取出的对象是B的哪一代父类,但是可以确定他一定是Object的子类,当我们添加的时候也只能添加B类和它的子类(假如我们能添加,此时添加一个Object对象,但是list2是A类容器)。
PECS原则
- 频繁往外读取内容的,适合用上界Extends。
- 经常往里插入的,适合用下界Super。