Unchecked Conversion 导致的 Java 方法返回类型变更

栏目: Java · 发布时间: 6年前

内容简介:在 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. 考虑 streamforEach 的定义为 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()


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

疯狂的站长

疯狂的站长

温世豪 / 清华大学出版社 / 2010年05月 / 29.00元

受全球性金融危机的影响,就业变得越来越困难,众多青年,包括大学毕业生,无不感到就业的巨大压力,站长这一职业不但创业门槛低,而且还自由自在。其实,搭建一个网站是相当简单的,但要成为一名成功的站长则不那么容易。 本书作者是一名站长,从事互联网相关工作已十余年,自已也在经营一个知名网站,积累了大量网站运营经验。作者结合自身真实的“疯狂”创业经历,以平实、通俗的语言讲述如何从零开始起步,最终成为一名......一起来看看 《疯狂的站长》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试