理解泛型通配符
近日在学kotlin,学到泛型时一脸懵逼,当初学Java时就没有彻底搞懂泛型通配符,趁这个机会好好理解
错误的理解
从前我使用<? extends T>
都是这么理解的。例如List<? extends Number>
那么就代表这个List可以存放 Number的子类,Integer、Double等。
直到有一次想起来 Integer作为 Number的子类,按理也可以放入List<Number>
之中。
那么List<? extends Number>
到底有什么不同呢,至于List<? super T>
我更是无法理解,不知所然。
先从原因说起
为何要有泛型通配符呢?实际上是为了方便类型转换,请看这个例子
1 | List<String> a = new ArrayList<>(); |
这个例子很好理解,a实际上是一个List<String>
,自然无法放入一个 Integer。
所以为了防止这种错误的写法的出现,规定List<String>
并不是List<Object>
的子类,故无法把List<String>
作为List<Object>
使用
但这样会出现许多问题,比如 List有个 addAll方法,它是这样的
1 | boolean addAll(Collection<E> c) { |
它的功能是把一个集合中所有元素添加进另一个集合,但如果你试图这么做就会出错
1 | List<Object> a = new ArrayList<>(); |
但是由于方法需要一个List<Object>
参数,而是b一个List<String>
,还记得前面说的结论吗,List<String>
不能当做使用List<Object>
。
然而我们很多时候都需要这种需求,因此通配符就出现了。
extends通配符
为了解决上述问题,出现了extends通配符
例如,List<? extends Object>
表示的意思是,这个 List存放着 Object的子类。
这个 List可能是List<String>
,也可能是List<Integer>
,无论是 Integer,或是 String,都是 Object的子类。
因此,调用List<? extends Object>
的get方法,能安全得到一个Object。
但由于我们并不知道这个 List是List<String>
还是List<Integer>
,因此往里面添加一个元素,可能就会出现问题。
所以声明为List<? extends Object>
后,能安全的从中获取,但无法向里面添加内容。
没有了安全问题,这种语法也就允许了。
1 | List<String> a = new ArrayList<>(); |
如果我将addAll改成这样
1 | boolean addAll(Collection<? extends E> c) { |
那么这些的代码就可以实现了
1 | List<Object> a = new ArrayList<>(); |
extends通配符相当于有更强的安全性,它使一个集合只读,同时也使它可以在一定程度上进行类型转换。
super通配符
与extends通配符相对应,extends是只读,那么super通配符就代表着只写,它能确保一个元素能安全写入某个集合。
1 | static <T> void fill(List<? super T> list, T t, int count) { |
这个方法可以这么调用
1 | fill(new ArrayList<String>(), "this is a text", 10); |
也可以这样
1 | fill(new ArrayList<Object>(), "this is a text", 10); |
这个例子中,T是String,故List的类型为List<? super String>
。
它意味着这个List应该是一个List
super保证了一个元素一定可以被写入一个集合中,而不用关心这个集合具体是什么类型。
更好的理解
在读kotlin文档时,有提到过生产者、消费者的概念。
生产者就是只能获取元素,不能消费(使用)元素的集合,对应着 extends
消费者就是只能消费元素,不能生产(获取)元素的集合,对应着 super
最初看这两个通配符确实很容易产生误解,因此 kotlin用了更直观的 out代表生产者,in代表消费者。
比如kotlin中不可变的数组就是 Array(out T)
,只能从这个不可变的数组中获取内容,而不能添加、修改、删除内容。
生产者消费者的概念更容易理解泛型,用这个方式就能十分轻松地理解许多地方使用通配符的原因。
总结
经过一段时间的思考,我对泛型的理解又深入一层,也发现了以前使用泛型时的许多错误。
顺便安利下kotlin,这门语言十分不错,有许多其他语言没有的特性。
Gogo有很长一段时间没有这种”学一门新语言”的感觉了。