内容简介:在 v2 遇到有同学反馈了个问题, 第一眼的感觉应该是泛型擦除(Type Erasure)和类型推断(Type Inference)导致的. 但当我尝试去彻底解释这个问题的时候, 才发现关键原因是: 如果在调用方法时有 unchecked conversion, 那么方法返回的是定义中返回类型经过擦除(erasure)后的结果.具体问题是这个样子的:如果对 type erasure, unchecked warning 不太熟悉, 可以先阅读后几节.
问题
在 v2 遇到有同学反馈了个问题, 第一眼的感觉应该是泛型擦除(Type Erasure)和类型推断(Type Inference)导致的. 但当我尝试去彻底解释这个问题的时候, 才发现关键原因是: 如果在调用方法时有 unchecked conversion, 那么方法返回的是定义中返回类型经过擦除(erasure)后的结果.
具体问题是这个样子的:
public static List<String> methodA(Collection<String> stringCollection) { List<String> stringList = new ArrayList<>(); for (String s : stringCollection) { stringList.add(s); } return stringList; } public static void methodB(String s) {} public static void main(String args[]) { // ok methodA((Collection<String>) new ArrayList<String>()).stream().forEach(p -> methodB(p)); // compile error // Question.java:29: 错误: 不兼容的类型: Object无法转换为String // methodA((Collection) map.get("A")).stream().forEach(p -> methodB(p)); // ^ methodA((Collection) new ArrayList<String>()).stream().forEach(p -> methodB(p)); }
猜测过程
如果对 type erasure, unchecked warning 不太熟悉, 可以先阅读后几节.
依我的理解, lambda 中 p 的类型应该被推断为 String. 考虑 stream
和 forEach
的定义为 Stream<E> Collection<E>.stream()
, void Stream<T>.forEach(Consumer<? super T> action)
. 整个类型推断的过程应该如下:
List<String> stringList = methodA(...); Stream<String> stringStream = stringList.stream(); stringStream.forEach(p -> methodB(p));
但从实际编译的结果来看, methodA((Collection<String>) new ArrayList<String>())
版本是符合预期的, 而 methodA((Collection) new ArrayList<String>())
版本中, p 的类型被推断为 Object.
有了两个版本的对比, 很容易就会去猜测是不是 mehtodA 返回的结果因为入参类型的不同而不同. 但tm我就走歪了, 因为映像中没有什么情况下方法返回类型会和定义不一致, 所以我先去排查了一遍泛型擦除, 类型推断, Java 对 Lambda 的处理方式等. 直到最后走头无路,才尝试去看返回类型是否有猫腻.
最终发现报错版本返回的结果类型为 raw type List
, 而不是预期的 parameterzied type List<String>
. 验证的代码如下:
Collection rawCollection = new ArrayList<String>(); // methodA 返回的是 raw type List, 此处赋值会因为 parameterzied type 指向 raw type 而导致 unchecked warning List<String> stringList = methodA(rawCollection);
官方的解释 是:
Otherwise, if unchecked conversion was necessary for the method to be applicable, then the result type is the erasure (§4.6) of the method's declared return type.
嗯, 报错版本的传入参数类型是 Collection
, 而 methodA 定义的参数类型为 Collection<String>
, 调用触发 unchecked conversion.
泛型擦除
Generic type 是泛型的定义, 由 Java 传统的类型结合 type parameter 组成.
通过提供具体的 type argument, generic type 实例化成为 parameterized type.
引用 Java Generic FAQs 中的内容, 泛型擦除指的是: 在编译过程中通过消除 type parameter 和 type argument 来将同一 generic type 对应的不同实例映射成同一个类的过程.
具体可以分为两个部分:
- Parameterized type 的 type parameters 被删除.
- Generic type 中的 type arguments 被替换为具体的类型.
在泛型擦除的过程中, 编译器可能按需加入额外的方法和类型转换来保证编译结果的正确性.
unchecked warning
uncheked warning 在编译期间产生, 表示编译器无法保证完全的类型安全, 当然也不代表一定类型不安全.
产生的几种场景基本都和泛型有关. 和本文关联的场景是: 将 parameterized type 指向 raw type, 比如 List<String> stringList = new ArrayList()
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- PyMiner 开源协议变更为 LGPL,技术变更为 PySide2
- 使用JGit获取变更细节
- 每日获取变更的CVE漏洞
- Raft 成员变更的工程实践
- 生产变更的几点感悟
- 需求变更控制有哪些先进技术?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
计算机科学概论(第7版) (平装)
J.Glenn Brookshear / 王保江 / 人民邮电出版社 / 2003-9 / 49.0
《计算机科学概论(第2版)》更新了部分内容,使其更加贴近于计算机科学领域内的最新趋势,这包括了网络安全、开源运动、关联存储、公钥加密、XML、Java和C#等内容。扩充了网络和Internet所覆盖的内容。一个程序用C#语言编写,还有C、C++和Java,作为语言的例子。不过整个方法依旧保持语言的独立。一起来看看 《计算机科学概论(第7版) (平装)》 这本书的介绍吧!