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()


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

查看所有标签

猜你喜欢:

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

Search User Interfaces

Search User Interfaces

Marti A. Hearst / Cambridge University Press / 2009-9-21 / USD 59.00

搜索引擎的本质是帮助用户更快、更方便、更有效地查找与获取所需信息。在不断改进搜索算法和提升性能(以技术为中心)的同时,关注用户的信息需求、搜寻行为、界面设计与交互模式是以用户为中心的一条并行发展思路。创新的搜索界面及其配套的交互机制对一项搜索服务的成功来说是至关重要的。Marti Hearst教授带来的这本新作《Search User Interfaces》即是后一条思路的研究成果,将信息检索与人......一起来看看 《Search User Interfaces》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具