点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者 | 刘一手

来源 | 公众号「锅外的大佬」

Java Generics – <?> vs <? extends Object>

相信很多人和我一样,接触Java多年,却仍旧搞不清楚 Java 泛型中 <?>和 <? extends Object>的相似和不同。但是,这应该是一个比较高端大气上档次的Question, 在我们进行深入的探讨之前,有必要对Java泛型有一个基础的了解。如果还不了解的,请看上一篇文章!

上一篇文章地址:(http://mp.weixin.qq.com/s?__biz=MzIzNzYxNDYzNw==&mid=2247484950&idx=1&sn=688f958d6f343ee2e314a724ee1129a3&chksm=e8c4a554dfb32c421bb1ac3e0fd461d997d82b68ffc8ea41367a71d537df4a7455af5810e3f4&scene=21#wechat_redirect)

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 errorfor (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 或其子类型(如IntegerDouble)的列表

  • 下界通配符:List<? super Integer> 表示Integer或其超类型NumberObject的列表

由于 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]

好了,文章到此就划上句号了,在本文中,我们主要讨论了<?> 和 <? extends Object>的异同,虽然基本上是相似的,但两者在可变与否方面存在细微差异。

DD自研的沪牌代拍业务,点击直达

【往期推荐】

索赔 100 万!只是因为一个开源插件?

2020-11-21

快速搞懂监控、链路追踪、日志三者的区别

2020-11-21

读完《Effective Java》后,总结了 50 条开发技巧

2020-11-20

35岁之后,你还会继续写代码吗?

2020-11-19

11月全国招程序员34万人,猜猜平均工资是多少?

2020-11-18

扫一扫,关注我

一起学习,一起进步

每周赠书,福利不断

深度内容

推荐加入

Java泛型中? 和 ? extends Object的异同分析相关推荐

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

    <? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. ...

  2. Java泛型中extends和super的理解(转)

    E – Element (在集合中使用,因为集合中存放的是元素) T – Type(Java 类) K – Key(键) V – Value(值) N – Number(数值类型) ? – 表示不确定 ...

  3. Java泛型中extends和super的区别?

    区别 <? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概 ...

  4. 聊一聊Java 泛型中的通配符 T,E,K,V,?

    点击上方"方志朋",选择"设为星标" 回复"1024"获取独家整理的学习资料 作者:glmapper juejin.im/post/5d57 ...

  5. 聊一聊-JAVA 泛型中的通配符 T,E,K,V,?

    前言 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许开发者在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据 ...

  6. Java泛型中的PECS原则

    今天在写代码的时候使用到了这样一个方法签名: public void foo(Map<String, String> map); 在写这个参数的时候正好在想一些关于泛型的东西,于是: pu ...

  7. JAVA 泛型中的通配符 T,E,K,V,?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群",加入新技术 来源:8rr.co/2Xqx 前言 Java 泛型(generic ...

  8. 一文读懂Java泛型中的通配符 ?

    之前不太明白泛型中通配符"?"的含义,直到我在网上发现了Jakob Jenkov的一篇文章,觉得很不错,所以翻译过来,大家也可以点击文末左下角的阅读原文看英文版的原文. 下面是我的 ...

  9. 原神一面:Java 泛型中的通配符 T,E,K,V,?,你确定都了解吗?

    点击上方 "编程技术圈"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 每日英文 Sometimes, the same thing, we can ...

最新文章

  1. [置顶] JDK工具(零)--简要介绍JDK1.6自带的42个工具
  2. 双网卡绑定之负载均衡高可用(bond)
  3. linux ftp上传下载文件,Linux下ftp命令上传下载文件
  4. tornado 学习笔记17 HTTPServerRequest分析
  5. Unity3D性能优化
  6. Linux学习总结(八)-磁盘格式化,挂载,swap扩容
  7. org.springframework.web.client.RestClientException: Could not extract response
  8. 从“救火”到“井然有序”
  9. socket编程详解,转自http://www.sme-cn.com:82/archives/669
  10. STM32固件库下载步骤
  11. mysql批量sql语句 c_Mysql批量插入数据sql语句
  12. 报计算机用几寸照片,在电脑显示的2寸照片的大小是几×几?
  13. Windows10 LTSB/LTSC 企业版安装应用商店
  14. Docker安装与镜像的使用
  15. USB OTG(Host) 、 USB ADB(Device)、DWC3 Charge
  16. 哈工大软件过程与工具复习5——第9讲 软件设计
  17. 函数柯里化与反柯里化
  18. 直接插入法(java实现)
  19. 2008 r2服务器日志文件,Windows2008R2共享文件访问日志查询的设置方法
  20. 【JZOJ】【匈牙利算法】【二分】 导弹

热门文章

  1. 很高兴加入51cto——交朋友
  2. docker 容器 defunct 僵尸进程
  3. windows下使用自带certutil工具校验文件MD5、SHA1、SHA256
  4. centos transmission 无法开启登录验证
  5. linux c 执行shell命令并获取返回结果
  6. 遍历创建进程、创建线程、加载模块的回调函数
  7. 在本机快速创建YUM源
  8. 利用 libvirt 和 Linux 审计子系统跟踪 KVM 客户机
  9. 数字媒体技术和数据科学与大数据技术_?数据科学与大数据技术的就业前景和待遇怎么样?...
  10. 湘潭大学网络编程_湘潭大学计算机学院网络空间安全学院“湘韵”研究生论坛成功举行...