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

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

内容简介:在 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()


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

查看所有标签

猜你喜欢:

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

移动交互设计精髓

移动交互设计精髓

班格 (Cameron Banga)、温霍尔德 (Josh Weinhold) / 傅小贞、张颖鋆 / 电子工业出版社 / 2015-4-1 / CNY 89.00

越来越多的人正涌入移动应用领域,而设计和体验将是移动应用成败的关键。作者通过上百款应用的设计实践,系统化地梳理了移动应用的设计方法论,在理解用户、跨平台和适配设计、移动组件应用、界面视觉感染力、简约设计等方面都进行了深入阐述。此外,作者还介绍了一些非常实用的移动设计工具,分享了设计师该如何与开发工程师协同工作,以及如何收集用户反馈、甄别版本迭代的更新需求等。 《移动交互设计精髓——设计完美的......一起来看看 《移动交互设计精髓》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具