Java警告“Unchecked Cast”

    1.概述

    有時,當我們編譯Java源文件時,會看到Java編譯器打印的“ unchecked cast ”警告消息。

    在本教程中,我們將仔細研究警告消息。我們將討論該警告的含義,被警告的原因以及如何解決問題。

    默認情況下,某些Java編譯器會抑制未經檢查的警告。

    確保在查看此“ unchecked cast ”警告之前,已啟用編譯器的選項以打印“ unchecked”警告。

    2.“ unchecked cast ”警告是什麼意思?

    unchecked cast ”是編譯時警告。簡而言之,當將原始類型轉換為參數類型而不進行類型檢查時我們會看到此警告

    一個例子可以直接解釋它。假設我們有一個簡單的方法來返回原始類型Map

    public class UncheckedCast {
    
     public static Map getRawMap() {
    
     Map rawMap = new HashMap();
    
     rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
    
     rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
    
     rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
    
     return rawMap;
    
     }
    
     ...
    
     }

    現在,讓我們創建一個測試方法來調用上面方法的方法,並將結果轉換為Map<String, LocalDate>

    @Test
    
     public void givenRawMap_whenCastToTypedMap_shouldHaveCompilerWarning() {
    
     Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMap();
    
     Assert.assertEquals(3, castFromRawMap.size());
    
     Assert.assertEquals(castFromRawMap.get("date 2"), LocalDate.of(1992, Month.AUGUST, 8));
    
     }
    

    編譯器必須允許此強制轉換保留與不支持泛型的舊Java版本的向後兼容性。

    但是,如果我們編譯Java源代碼,則編譯器將打印警告消息。接下來,讓我們使用Maven編譯並運行單元測試:

    $ mvn clean test
    
     ...
    
     [WARNING] .../src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java:[14,97] unchecked cast
    
     required: java.util.Map<java.lang.String,java.time.LocalDate>
    
     found: java.util.Map
    
     ...
    
     [INFO] -------------------------------------------------------
    
     [INFO] TESTS
    
     [INFO] -------------------------------------------------------
    
     ...
    
     [INFO] Results:
    
     [INFO]
    
     [INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0
    
     [INFO]
    
     [INFO] ------------------------------------------------------------------------
    
     [INFO] BUILD SUCCESS
    
     [INFO] ------------------------------------------------------------------------
    
     ...
    

    如Maven輸出所示,我們已成功重現了警告。

    另一方面,即使我們看到“ unchecked cast ”編譯器警告,我們的測試也沒有任何問題。

    我們知道編譯器不會無故警告我們。當我們看到此警告時,肯定有一些潛在的問題。

    讓我們弄清楚。

    3.為什麼Java編譯器會警告我們?

    儘管我們看到“ unchecked cast ”警告,但我們的測試方法在上一節中運行良好。這是因為當我們將原始類型MapMap<String, LocalDate> ,原始Map僅包含<String, LocalDate>條目。也就是說,類型轉換是安全的。

    為了分析潛在的問題,讓我們通過向原始類型Map添加一個條目來稍微更改getRawMap()方法:

    public static Map getRawMapWithMixedTypes() {
    
     Map rawMap = new HashMap();
    
     rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
    
     rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
    
     rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
    
     rawMap.put("date 4", new Date());
    
     return rawMap;
    
     }
    

    這次,我們在上述方法中為<String, Date>類型的Map添加了一個新條目。

    現在,讓我們編寫一個新的測試方法來調用getRawMapWithMixedTypes()方法:

    @Test(expected = ClassCastException.class)
    
     public void givenMixTypedRawMap_whenCastToTypedMap_shouldThrowClassCastException() {
    
     Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes();
    
     Assert.assertEquals(4, castFromRawMap.size());
    
     Assert.assertTrue(castFromRawMap.get("date 4").isAfter(castFromRawMap.get("date 3")));
    
     }

    如果我們編譯並運行測試,則會再次打印“ unchecked cast ”警告消息。另外,我們的測試將通過。

    但是,由於我們的測試具有expected = ClassCastException.class參數,所以這意味著測試方法拋出了ClassCastException

    如果我們仔細研究一下,儘管警告消息指向此行**Map<String, LocalDate>但在將原始類型MapMap<String, LocalDate>的行上不會拋出ClassCastException** 。相反,當我們通過鍵castFromRawMap.get(“date 4”).獲取類型錯誤的數據時,會發生異常castFromRawMap.get(“date 4”).

    如果我們將包含錯誤類型數據的原始類型集合轉換為參數化類型集合,則在加載錯誤類型的數據之前,不會拋出ClassCastException

    有時,我們可能太遲才得到例外。

    例如,通過調用我們的方法,我們獲得了包含許多條目的原始Map ,然後將其轉換為參數化類型的Map

    (Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes()

    對於Map中的每個條目,我們需要將LocalDate對象發送到遠程API。在我們遇到ClassCastException ,很可能已經進行了許多API調用。根據要求,可能會涉及一些額外的還原或數據清除過程。

    如果我們能夠儘早獲得異常,那將是一個好習慣,這樣我們就可以決定如何處理類型錯誤的條目的情況。

    當我們了解“ unchecked cast ”警告背後的潛在問題時,讓我們看看我們可以採取什麼措施來解決該問題。

    4.我們應該如何處理警告?

    4.1。避免使用原始類型

    從Java 5開始引入泛型。如果我們的Java環境支持泛型,則應避免使用原始類型。這是因為使用原始類型將使我們失去泛型的所有安全性和表達優勢。

    此外,我們應該搜索遺留代碼,並將那些原始類型的用法重構為泛型。

    但是,有時我們必須使用一些舊庫。這些舊外部庫中的方法可能返回原始類型集合。

    調用這些方法並將其轉換為參數化類型將產生“ unchecked cast ”編譯器警告。但是我們無法控制外部庫。

    接下來,讓我們看一下如何處理這種情況。

    4.2。禁止“ unchecked ”警告

    如果我們無法消除“ unchecked cast ”警告,並且確定引起警告的代碼是類型安全的,則可以使用SuppressWarnings(“unchecked”)註釋來消除警告。

    當使用@ SuppressWarning(“unchecked”)批註時,應始終將其放在盡可能小的範圍內。

    讓我們以ArrayList類中的remove()方法為例:

    public E remove(int index) {
    
     Objects.checkIndex(index, size);
    
     final Object[] es = elementData;
    
    
    
     @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    
     fastRemove(es, index);
    
    
    
     return oldValue;
    
     }

    4.3。使用原始類型集合之前進行類型安全檢查

    如我們@SuppressWarning(“unchecked”)@SuppressWarning(“unchecked”)註釋僅抑制警告消息,而沒有實際檢查@SuppressWarning(“unchecked”)類型轉換是否是類型安全的。

    如果不確定轉換原始類型是否是類型安全的,則應在實際使用數據之前檢查類型,以便更早獲得ClassCastException

    5.結論

    在本文中,我們了解了“ unchecked cast ”編譯器警告的含義。

    此外,我們已經解決了此警告的原因以及如何解決潛在的問題。