List<Number> numbers = new ArrayList<Integer>();
java的ArrayList有一个这样的构造函数
public ArrayList(Collection<? extends E> c) {
.......
}
在不知道extends的作用的前提下,我们日常使用中就已经明白了这个构造函数的意思是传入一个Collection类型的集合,里面包含了我们声明的元素或者其子元素
用代码来说就是:
ArrayList<Integer> integers = new ArrayList<>();
ArrayList<Double> doubles = new ArrayList<>();
new ArrayList<Number>(integers);
new ArrayList<Number>(doubles);
想在已有的List的基础上去创建一个包含Number的List,因为是包含Number的集合,所以理论上我我可以传入List<Integer>,List<Double>这样的包含任一Number的子类的List。如果不去看源码的情况下,我们可能会很理想化的想到,ArrayList的这个构造函数是这样的:
public ArrayList(Collection<E> c) { ....... }
代表我可以传入List<Integer>,List<Double>。然而并不是。
因为在java里面,
List<Number> list = new ArrayList<Integer>();
所以有了extends。
List<? extends Number> list;
list = new ArrayList<Number>();
list = new ArrayList<Integer>();
list = new ArrayList<Double>();
以上便是extends的作用,它表示声明的这个list 可以被赋予任何一种包含Number和Number子类的list
因为被赋予的List包含的必定是Number的子类,所以读出来的元素不论原本是Integer,Double,还是任一Number的子类,都可以像上转型为Number,所以下面的代码是没问题的。
Number item = list.get(0);
但是,当你add的时候,因为被赋予有可能是new ArrayList<Integer>() ,new ArrayList<Double>()这些List中的任一一个,光靠声明的信息JVM在编译的时候是不知道具体是哪个list,那么你就不能往里面加元素。因为如果是赋予的一个new ArrayList<Integer>(),而你add的是一个Double,那岂不是违反了泛型的基本语法,不允许不同类型的元素放入泛型容器中。
list.add(??????);
所以,当在声明的时候使用了extends,那么List就只能get,不能add了。
也即 extends 表示此list只能读。
super,同样的Demo:
List<? super Double> list;
list = new ArrayList<Double>();
list = new ArrayList<Number>();
list = new ArrayList<Object>();
上面这代码表示list表示可以被赋予任意一个装有Double或Double父类的list;
同理
读:
???? item = list.get(0);
这样是不行的,为啥?因为这个list是可能被赋予的list太广了。
如果是List<? super Double> list = new ArrayList<Object>();
那怎么可能知道你读出来的会是什么玩意儿?
写:
list.add(new Double(1));
为啥这里可以写入呢?
因为不论赋予的是以下3种的任一一种集合,反正都是最少都是装Double的,所以一定可以装入Double,那么写入是可以的。
list = new ArrayList<Double>();
list = new ArrayList<Number>();
list = new ArrayList<Object>();
所以使用了super的list没法读取,只能写入。
结合extends和super的特性,总结出来就是:PECS(Producer Extends, Consumer Super)
参考link:https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java
网友评论