0%

Java 泛型中 ? super T 和 ? extends T 的区别

最近在看阿里出的一本书 —— 《码出高效 Java 开发手册》,这本书不是之前那本关于 Java 开发注意规范的手册,这本书内容还是不错的,干货满满。

在看书的时候,就看到了关于上面讲的知识点,于是想到之前在看 Java 一些源码时候,也看到一些关于这个泛型的写法,当时还没注意,比如下面关于 ThreadLocal 源码是看到的。

这样以后看 Java 的源码就能看懂这些泛型的写法了。

是 Java 泛型中的 “通配符“和”边界”的概念。 - : 是指“上界通配符”。 A super B 表示 A 是 B 的父类或者祖先,在 B 上面。 - :是指“下界通配符”。A extends B 表示 A 是 B 的子类或者孙类,在 B 的下面。 两种语法,但是两者的区别非常微妙。简单来说,是 Get first,适用于消费集合元素为主的场景;是 Put First,适用于生产集合元素为主的场景。 # 可以赋值给任何 T 及 T 子类的集合,上界为T,取出来的类型带有泛型限制,向上强制转型为T。null可以表示任何类型,所以除nul外,任何元素都不得添加进集合内。 # 可以赋值给任何 T 及 T 的父类集合,下界为T。在生活中,投票选举类似于 的操作。选举代表时,你只能往里投选票,取数据时,根本不知道是谁的票,相当于泛型丢失。有人说,这只是一种生活场景,在系统设计中。很难有这样的情形。再举例说明一下,我们在填写对主管的年度评价时,提交后若想再次访问之前的链接修改评价,就会被告之:“您已经完成对主管的年度反馈,谢谢参与。" extends的场景是put功能受限,而 super的场景是get功能受限。 # 集合和泛型 下面的代码和说明出自《码出高效,Java开发手册》
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static void main(String[] args) {

//第一阶段:泛型出现之前的集合定义方式
List a1 = new ArrayList();
a1.add(new Object());
a1.add(new Integer(111));
a1.add(new String("Hello World"));

for (Object o : a1){
System.out.println(o);
}

//第二段:把a1 引用赋值给a2,注意a2与a1的区别是增加了泛型限制<Object>
List<Object> a2 = a1;
a2.add(new Object());
a2.add(new Integer(222));
a2.add(new String("hello a2a2"));

//第三段:把a1引用赋值给a3,注意a3与a1的区别是增加了泛型<Integer>
List<Integer> a3 = a1;
a3.add(new Integer(333));
//下方两行编译出错,不允许增加非 Integer类型进入集合
a3.add(new Object());
a3.add(new String("hello a3a3"));

//第四段:把a1引用赋值给a4,a1与a4的区别是增加了通配符
List<?> a4 = a1;
a1.remove(0);
a4.clear();
//编译出错。不允许增加任何元素
a4.add(new Object());

}
第一段说明:在定义List之后,毫不犹豫地往集合里装入三种不同的对象:Object、 Integer 和 String,遍历没有问题,但是贸然以为里边的元素都是 Integer,使用强制转化,则抛出 ClassCastException异常。 第二段说明:把a赋值给a2,a2是 List类型的,也可以再往里装入三种不同的对象。很多程序员认为List和List是完全相同的,至少从目前这两段来看是这样的。 第三段说明:由于泛型在JDK5之后才出现,考虑到向前兼容,因此历史代码有时需要赋值给新泛型代码,从编译器角度是允许的。这种代码似乎有点反人类,在实际故障案例中经常出现。 第四段说明:问号在正则表达式中可以匹配任何字符,List

称为通配符集合。它可以接受任何类型的集合引用赋值,不能添加任何元素,但可以 remove 和 clear,并非 immutable集合。List<?>一般作为参数来接收外部的集合,或者返回一个不知道具体元素类型的集合。

Reference

  • 《码出高效:Java开发手册》
客官,赏一杯coffee嘛~~~~