Java泛型中<?> 和 <? extends Object>的异同分析
相信很多人和我一样,接触Java多年,却仍旧搞不清楚 Java
泛型中 <?>
和 <? extends Object>
的相似和不同。但是,这应该是一个比较高端大气上档次的Question, 在我们进行深入的探讨之前,有必要对Java泛型有一个基础的了解。详细请看上一篇文章!
1. 泛型产生的背景
在 JDK5 中引入了泛型来消除编译时错误和加强类型安全性。这种额外的类型安全性消除了某些用例中的强制转换,并使程序员能够编写泛型算法,这两种方法都可以生成更具可读性的代码。
例如,在 JDK5 之前,我们必须使用强制转换来处理列表的元素。这反过来又产生了一类特定的运行时错误:
List aList = new ArrayList();
aList.add(new Integer(1));
aList.add("a_string");
for (int i = 0; i < aList.size(); i++) {
Integer x = (Integer) aList.get(i);
}
现在,我们想解决两个问题:
- 我们需要一个显式转换来从
aList
中提取值——类型取决于左侧的变量类型(在本例中为Integer) - 当我们试图将
a_string
转换为Integer
时,在第二次迭代中会出现运行时错误。
泛型填补了这个空白,代码如下:
List<Integer> iList = new ArrayList<>();
iList.add(1);
iList.add("a_string"); // compile time error
for (int i = 0; i < iList.size(); i++) {
int x = iList.get(i);
}
执行上述代码,编译器会告诉我们,无法将 a_string 添加到 Integer 类型的 List 中,这比起在运行时才发现异常要好很多。
而且,不需要显式转换,因为编译器已经知道 iList 包含 Integer类型的数据。另外,由于自动拆箱的关系,我们甚至不需要使用 Integer 类型,它的原始类型就足够了。
2. 泛型中的通配符
问号或通配符在泛型中用来表示未知类型。它可以有三种形式:
- 无界通配符: List<?> 表示未知类型的列表
- 上界通配符:List<? extends Number> 表示 Number 或其子类型(如Integer和Double)的列表
- 下界通配符:List<? super Integer> 表示Integer或其超类型Number和Object的列表
由于 Object 是 Java 中所有类型的固有超类,所以我们会认为它也可以表示未知类型。换句话说,List<?>
和List<Object>
可以达到相同的目的。但事实并非如此。
来看看这两个方法:
public static void printListObject(List<Object> list) {
for (Object element : list) {
System.out.print(element + " ");
}
}
public static void printListWildCard(List<?> list) {
for (Object element: list) {
System.out.print(element + " ");
}
}
给出一个整数的列表,比如:
List<Integer> li = Arrays.asList(1, 2, 3);
执行 printListObject(li)
不会编译,并且我们将得到以下错误:
The method printListObject(List<Object>) is not applicable for the arguments (List<Integer>)
而执行 printListWildCard(li)
将通过编译,并将 1 2 3 输出到控制台。
3. <?>和<? extends Object>的相同之处
在上面的示例中,如果我们将 printListWildCard 方法更改为:
public static void printListWildCard(List<? extends Object> list)
它的工作方式与 printListWildCard(List<?>)
相同。这是因为 Object 是 Java 所有对象的超类,基本上所有的东西都扩展了Object。因此,这个方法也会处理一个 Integer 类型的List。
也就是说, <?> 和 <? extends Object> 在这个例子中是同一个意思。
虽然在大多数情况下,这是正确的,但也有一些区别。接下来我们就来看看它们之间的差异。
4. <?>和<? extends Object>的不同之处
可重构类型是指那些在编译时未被擦除的类型。换句话说,一个不可重构类型,运行时将比编译时表达的信息更少,因为其中一些信息会被擦除。
一般来说,参数化类型是不可重新定义的。比如 List<String>
和 Map<Integer,String>
就不可重新定义。编译器会擦除它们的类型,并将它们分别视为列表和映射。
这个准则的唯一例外是无界通配符类型。也就是说, List<?>
以及 Map<?, ?>
是可重写的。
另外,List<? extends Object>
不可重写。虽然微妙,但这是一个显著的区别。
不可重构的类型在某些情况下不能使用,例如在 instanceof
运算符或作为数组的元素。
所以,如果我们的代码写成这样:
List someList = new ArrayList<>();
boolean instanceTest = someList instanceof List<?>
代码编译后,instanceTest 为true。
但是,如果我们在 List<? extends Object>
上使用 instanceof 运算符:
List anotherList = new ArrayList<>();
boolean instanceTest = anotherList instanceof List<? extends Object>;
那么第2行不编译。
类似地,在下面的代码片段中,第1行编译,但第2行不编译:
List<?>[] arrayOfList = new List<?>[1];
List<? extends Object>[] arrayOfAnotherList = new List<? extends Object>[1]
5.结语
好了,文章到此就划上句号了,在本文中,我们主要讨论了<?> 和 <? extends Object>的异同,虽然基本上是相似的,但两者在可变与否方面存在细微差异。
如果你觉得文章还不错,记得关注公众号: 锅外的大佬
刘一手的博客
Java泛型中<?> 和 <? extends Object>的异同分析的更多相关文章
- Java泛型中<? extends E>和<? super E>的区别
这篇文章谈一谈Java泛型声明<? extends E>和<? super E>的作用和区别 <? extends E> <? extends E> 是 ...
- 浅谈Java泛型中的extends和super关键字(转)
通配符 在本文的前面的部分里已经说过了泛型类型的子类型的不相关性.但有些时候,我们希望能够像使用普通类型那样使用泛型类型: 向上造型一个泛型对象的引用 向下造型一个泛型对象的引用 向上造型一个泛型对象 ...
- 浅谈Java泛型中的extends和super关键字
泛型是在Java 1.5中被加入了,这里不讨论泛型的细节问题,这个在Thinking in Java第四版中讲的非常清楚,这里要讲的是super和extends关键字,以及在使用这两个关键字的时候为什 ...
- Java泛型中的extends和super关键字
理解List<? extends T> list, T key, Comparator<? super T> c 这些一般用在方法形参类型上,用于接受泛型对象. 1.List& ...
- Java泛型中的协变和逆变
Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛 ...
- Java泛型中extends和super的理解(转)
E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类) K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定 ...
- Java泛型中extends和super的区别?
<? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. ...
- 【转】聊一聊-JAVA 泛型中的通配符 T,E,K,V,?
原文:https://juejin.im/post/5d5789d26fb9a06ad0056bd9 前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型 ...
- Java泛型中的通配符T,E,K,V
Java泛型中的通配符T,E,K,V 1.泛型的好处 2.泛型中的通配符 2.1 T,E,K,V,? 2.2 ?无界通配符 2.3 上界通配符 < ? extends E> 2.4 下界通 ...
随机推荐
- h5的第一份翻译
<!DOCTYPE html>DOCTYPE DOC文本文档documentTYPE 类型html hyper超,超级的:text文本:markup标记:language语言<htm ...
- echo输出彩色文字
开启转义功能 echo -e表示开启转义功能,比如: 彩色文字语法 echo -e "\e[前景;背景;特效m""hello""\e[0m" ...
- 基于python实现链式栈
""" 链式栈 linkstack.py 思路分析: 1.源于链表结构 2.封装栈的操作方法(入栈,出栈,栈空,栈顶) 3.链表的开头作为栈顶(不用每次遍历,效率高,怎样 ...
- centos平台scp通过密钥远程复制文件(免密登录)
一,说明:两台机器的平台和ip 1,a服务器: centos8:ip:121.122.123.47 版本 [root@yjweb ~]# cat /etc/redhat-release CentOS ...
- springboot打成jar包和war包的两种方式,并引入第三方jar包!
springboot打成jar包和war包的两种方式,并引入第三方jar包! 首先把需要引入的第三方jar包引入到本地项目中,在引用的模块下加一个lib文件夹 一.打成jar包 1.修改pom文件里的 ...
- FDDB人脸检测数据集 生成ROC曲线
看了好多博客,踩了很多坑,终于把FDDB数据集的ROC曲线绘制出来了.记录一下. 环境:ubuntu18.04 1.数据集准备 去FDDB官网:http://vis-www.cs.umass.edu/ ...
- Linux终端 terminal
终端 一个可以进行人机交互的界面 物理终端 设备终端:键盘.鼠标.显示器 终端类型 控制台终端: /dev/console 串行终端:/dev/ttyS# 虚拟终端:tty:teletypewrite ...
- 技术实操丨HBase 2.X版本的元数据修复及一种数据迁移方式
摘要:分享一个HBase集群恢复的方法. 背景 在HBase 1.x中,经常会遇到元数据不一致的情况,这个时候使用HBCK的命令,可以快速修复元数据,让集群恢复正常. 另外HBase数据迁移时,大家经 ...
- Redis实现缓存与分布式锁
缓存与分布式锁 哪些数据适合放入缓存 即时性.数据一致性要求不高的 访问量大且更新频率不高的数据 选择redis做为缓存中间件 <dependency> <groupId>or ...
- 笔记本电脑插网线能上网,但是连不上WIFI,或者连上WiFi显示无internet,怎么解决?
1.鼠标 右键 "此电脑",选择"属性"