相信有很多朋友已经发现在JAVA中下面的代码会报错,但却不知道为什么,百度搜来搜去也都没有一个完全能够让人信服的解释。
public class Tester{
public static void main(String[] args) {
List<? extends Chinese> list = new ArrayList<ZhangSan>();
// Error
// list.add(new ZhangSan());
}
}
public class Human{}
public class Chinese extends Human{}
public class ZhangSan extends Chinese{}
public class LiSi extends Chinese{}
其实原因并不复杂。
首先我们要理解List<? extends Chinese>的含义,以及它和List<Chinese>的异同。
它们的相同之处在于都代表一个可以存放Person及其子类的List。
它们的不同之处在于List<? extends Chinese> list = new ArrayList<ZhangSan>()是可以编译通过的,而List<Chinese> list = new ArrayList<ZhangSan>()是无法通过编译的(至于为什么,网上有很多解说,比如 Java 泛型 <? super T> 中 super 怎么 理解?与 extends 有何不同?)。
好像说了个废话。
关键点来了,注意List<? extends Chinese> list只是声明,而new ArrayList<ZhangSan>()才是真正的具体的实现,Chinese类很可能不止一个子类,比如上例中除了ZhangSan还有LiSi,如果编译器不禁止add方法,那么很可能你会这么做:
List<? extends Chinese> list = new ArrayList<ZhangSan>();
list.add(new LiSi());
注意注意注意!!!你往ArrayList<ZhangSan>()里面放了个LiSi!!!如果编译器允许你这么做,会导致什么后果呢?
如果你真的想要一个既能放ZhangSan又能放LiSi的List,那正确的做法应该是
List<Chinese> list = new ArrayList<>();
同理,为什么下面代码反而能正常运行(看声明好像是只能放Chinese的祖宗,但事实上只能放Chinese的子孙),大家应该就能理解了吧。
List<? super Chinese> list = new ArrayList<Human>();
list.add(new LiSi());
list.add(new Chinese());
// Error
// list.add(new Human());