摘要:問題在遇到有同學反饋了個問題第一眼的感覺應該是泛型擦除和類型推斷導致的但當我嘗試去徹底解釋這個問題的時候才發現關鍵原因是如果在調用方法時有那么方法返回的是定義中返回類型經過擦除后的結果具體問題是這個樣子的錯誤不兼容的類型無法轉換為猜測
問題
在 v2 遇到有同學反饋了個問題, 第一眼的感覺應該是泛型擦除(Type Erasure)和類型推斷(Type Inference)導致的. 但當我嘗試去徹底解釋這個問題的時候, 才發現關鍵原因是: 如果在調用方法時有 unchecked conversion, 那么方法返回的是定義中返回類型經過擦除(erasure)后的結果.
具體問題是這個樣子的:
public static List猜測過程methodA(Collection stringCollection) { List 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 ) new ArrayList ()).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 ()).stream().forEach(p -> methodB(p)); }
如果對 type erasure, unchecked warning 不太熟悉, 可以先閱讀后幾節.
依我的理解, lambda 中 p 的類型應該被推斷為 String. 考慮 stream 和 forEach 的定義為 Stream
ListstringList = methodA(...); Stream stringStream = stringList.stream(); stringStream.forEach(p -> methodB(p));
但從實際編譯的結果來看, methodA((Collection
有了兩個版本的對比, 很容易就會去猜測是不是 mehtodA 返回的結果因為入參類型的不同而不同. 但tm我就走歪了, 因為映像中沒有什么情況下方法返回類型會和定義不一致, 所以我先去排查了一遍泛型擦除, 類型推斷, Java 對 Lambda 的處理方式等. 直到最后走頭無路,才嘗試去看返回類型是否有貓膩.
最終發現報錯版本返回的結果類型為 raw type List, 而不是預期的 parameterzied type List
Collection rawCollection = new ArrayList(); // methodA 返回的是 raw type List, 此處賦值會因為 parameterzied type 指向 raw type 而導致 unchecked warning List 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
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 warninguncheked warning 在編譯期間產生, 表示編譯器無法保證完全的類型安全, 當然也不代表一定類型不安全.
產生的幾種場景基本都和泛型有關. 和本文關聯的場景是: 將 parameterized type 指向 raw type, 比如 List
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73069.html
泛型原始類型 原始類型是沒有任何類型參數的泛型類或接口的名稱,例如,給定Box泛型類: public class Box { public void set(T t) { /* ... */ } // ... } 要創建參數化類型的Box,請為形式類型參數T提供實際類型參數: Box intBox = new Box(); 如果省略實際的類型參數,則創建一個原始類型Box: Box...
摘要:異常處理作用調試程序定位缺陷。對異常的處理是系統容錯可靠性的一環。拋出需要具體子異常捕獲同時也需要具體子異常不同子異常處理會不同。如有基礎基類異常調用者可以選擇使用這個基類該類下面的所有子異常使用統一處理也可以單獨對子異常處理。 showImg(https://segmentfault.com/img/bVbrm5g); 1 背景 1年前的文章,源于Sonar靜態代碼掃描中,項目歷史代...
摘要:知識點總結注解內置注解知識點總結注解定義在中,此注釋只適用于修飾方法,表示一個方法聲明打算重寫父類的另一個方法聲明。此注釋可用于修飾方法屬性類,表示不鼓勵程序員使用這樣的元素,通常是因為它很危險或存在更好的選擇。 Java知識點總結(注解-內置注解) @(Java知識點總結)[Java, 注解] @Override 定義在java.lang.Override 中,此注釋只適用于修飾方法...
類型推斷 類型推斷是Java編譯器查看每個方法調用和相應聲明的能力,以確定使調用適用的類型參數,推理算法確定參數的類型,如果可用,還確定分配或返回結果的類型,最后,推理算法嘗試查找適用于所有參數的最具體類型。 為了說明最后一點,在下面的示例中,推斷確定傳遞給pick方法的第二個參數是Serializable類型: static T pick(T a1, T a2) { return a2; } ...
摘要:然而,我更傾向于使用來單元測試來文檔化異常。單元測試允許我在使用中查看異常,并且作為一個可以被執行的文檔來使用。通過為異常編寫單元測試,你不僅可以記錄異常如何觸發,還可以使你的代碼在經過這些測試后更加健壯。 本文是關于 Exception 處理的一篇不錯的文章,從 Java Exception 的概念介紹起,依次講解了 Exception 的類型(Checked/Unchecked),...
閱讀 1961·2021-09-09 09:33
閱讀 1107·2019-08-30 15:43
閱讀 2646·2019-08-30 13:45
閱讀 3297·2019-08-29 11:00
閱讀 845·2019-08-26 14:01
閱讀 3559·2019-08-26 13:24
閱讀 471·2019-08-26 11:56
閱讀 2683·2019-08-26 10:27