1700447206
编写高质量代码:改善Java程序的151个建议 建议96:不同的场景使用不同的泛型通配符
1700447207
1700447208
Java泛型支持通配符(Wildcard),可以单独使用一个“?”表示任意类,也可以使用extends关键字表示某一个类(接口)的子类型,还可以使用super关键字表示某一个类(接口)的父类型,但问题是什么时候该用extends,什么时候该用super呢?
1700447209
1700447210
(1)泛型结构只参与“读”操作则限定上界(extends关键字)
1700447211
1700447212
阅读如下代码,想想看我们的业务逻辑操作是否还能继续:
1700447213
1700447214
public static<E>void read(List<?super E>list){
1700447215
1700447216
for(Object obj:list){
1700447217
1700447218
//业务逻辑操作
1700447219
1700447220
}
1700447221
1700447222
}
1700447223
1700447224
从List列表中读取元素的操作(比如一个数字列表中的求和计算),你觉得方法read能继续写下去吗?
1700447225
1700447226
答案是:不能,我们不知道list到底存放的是什么元素,只能推断出是E类型的父类(当然,也可以是E类型,下同,不再赘述),但问题是E类型的父类是什么呢?无法再推断,只有运行时才知道,那么编码期就完全无法操作了。当然,你可以把它当作是Object类来处理,需要时再转换成E类型—这完全违背了泛型的初衷。
1700447227
1700447228
在这种情况下,“读”操作如果期望从List集合中读取数据就需要使用extends关键字了,也就是要界定泛型的上界,代码如下:
1700447229
1700447230
public static<E>void read(List<?extends E>list){
1700447231
1700447232
for(E e:list){
1700447233
1700447234
//业务逻辑处理
1700447235
1700447236
}
1700447237
1700447238
}
1700447239
1700447240
此时,已经推断出List集合中取出的是E类型的元素。具体是什么类型的元素就要等到运行时才能确定了,但它一定是一个确定的类型,比如read(Arrays.asList(“A”))调用该方法时,可以推断出List中的元素类型是String,之后就可以对List中的元素进行操作了,如加入到另外的List<E>集合中,或者作为Map<E, V>的键等。
1700447241
1700447242
(2)泛型结构只参与“写”操作则限定下界(使用super关键字)先看如下代码是否可以编译:
1700447243
1700447244
public static void write(List<?extends Number>list){
1700447245
1700447246
//加入一个元素
1700447247
1700447248
list.add(123);
1700447249
1700447250
}
1700447251
1700447252
编译失败,失败的原因是list中的元素类型不确定,也就是编译器无法推断出泛型类型到底是什么,是Integer类型?是Double?还是Byte?这些都符合extends关键字的定义,由于无法确定实际的泛型类型,所以编译器拒绝了此类操作。
1700447253
1700447254
在此种情况下,只有一个元素是可以add进去的:null值,这是因为null是一个万用类型,它可以是所有类的实例对象,所以可以加入到任何列表中。
[
上一页 ]
[ :1.700447205e+09 ]
[
下一页 ]