最近写代码时,使用了泛型的容器类,但是却一直发现编译报错.搜到一片文章 Java通配符与extends/super的PECS原则,感觉还是不太理解. 自己写了一段代码测试下:
class A {
}
class B extends A {
}
class C extends B {
}
class D extends C {
}
public class Test {
public static void main(String[] args) {
List<? extends B> extendsList = new ArrayList<>();
extendsList.add(new C());
B b = extendsList.get(0);
List<? super B> superList = new ArrayList<>();
superList.add(new C());
B b1 = superList.get(0);
}
}
extendsList.add(new C());
会报错
B b1 = superList.get(0);
也会报错的
? extends B
extendsList
的元素类型为 B或B的子类. 所以extendsList
可以是List<B>,List<C>,List<D>,或者任何继承自B类的子类.
-
当add时,因为允许
extendsList
为List<D>,所以添加一个C的话,会报错.而且因为extendsList
的类型可能为B的任意子类(类型不确定),所以添加任意类型都会报错(包括new Object()). -
当get时,元素为B或B的子类,都可以安全向上转型为B(A a = extendsList.get(0)也是可以的.),所以编译可以成功.
? super B
superList
的元素类型为 B或B的父类. 所以superList
可以是List<B>,也可以是List<A>,或者List<Object>.
-
当add时,因为
superList
为B和B的父类,所以添加B和B的子类(C,D)时,是可以向上转型为B和B的父类,编译可以成功. -
当get时,元素为B或B的父类,B的父类是不能转型为B,所以编译报错.但是所有的类都可以转型为Object,所以可以使用Object o = superList.get(0);
PECS原则
- 如果想从集合get元素,并限制add元素(不可以add任意类型的元素),则可以使用<? extends T>,限制上界(Producer extends, 生产者只有get操作,不能add)。
- 如果想给集合add元素,并限制get元素(get的元素只能是Object),则可以使用<? super T>,限制下界(Consumer super, 消费者只能add,不能get)。
- 如果不想限制get和add,则不用通配符,List<T>就可以。