首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 謎題精選(一):Set 泛型與自動裝箱類型陷阱

     編輯:Java 相關知識

謎題代碼

import java.util.*;

public class ShortSet {
    public static void main(String[] args) {
        Set<Short> s = new HashSet<Short>();
        for (short i = 0; i < 100; i++) {
            s.add(i);
            s.remove(i - 1);
        }
        System.out.println(s.size());
    }
}

選項

  • (a) 1
  • (b) 100
  • (c) Throws exception
  • (d) None of the above

正確答案:(b) 100

深度解析:類型不匹配導致 remove 無效

關鍵代碼行

s.remove(i - 1);  // 陷阱在此!

類型推導鏈

  1. i 類型為 short
  2. 1 為 int 字面量
  3. short - int → **整數提升** → 結果類型為 int
  4. s.remove() 參數類型為 Object (非泛型 E)
  5. int 自動裝箱為 Integer 對象
  6. Set<Short> 內部存儲的是 Short 對象
  7. Short 不等於 Integer,即使數值相同,equals() 返回 false

泛型接口定義揭示設計意圖

public interface Set<E> extends Collection<E> {
    boolean add(E e);           // 類型安全:必須是 E 或子類型
    boolean remove(Object o);   // 故意設計為 Object:兼容舊版非泛型代碼
    // ...
}
  • add() 編譯期檢查類型,傳入 Integer 會報錯
  • remove() 接受 Object,**編譯器無法檢測類型錯誤**
  • 這是 Java 5 泛型化為保持向後兼容做出的妥協

執行流程模擬

迭代add(i)remove(i-1)Set 狀態
i=0添加 Short(0)移除 Integer(-1) → 不存在{0}
i=1添加 Short(1)移除 Integer(0) → 不匹配 Short(0){0, 1}
i=2添加 Short(2)移除 Integer(1) → 不匹配 Short(1){0, 1, 2}
............
i=99添加 Short(99)移除 Integer(98) → 不匹配 Short(98){0..99} 共 100 個

修正方案

s.remove((short)(i - 1));  // 強制轉型為 short,自動裝箱為 Short

修正後輸出:(a) 1 (只有最後添加的 99 未被移除)

核心經驗教訓

  1. 混合整數類型運算結果為 int/long:byte/short/char 與 int/long 運算自動提升為 int/long
  2. 自動裝箱保留原始類型語義:int → Integer,short → Short,**不會自動縮窄轉換**
  3. 集合操作類型檢查不對稱:add() 嚴格,remove()/contains() 寬鬆 (Object)
  4. 優先使用 int/long:除非內存/存儲極度敏感,否則避免 byte/short 參與運算
  5. 單元測試覆蓋邊界類型轉換:專門測試 remove(Object) 類型不匹配場景

擴展:相關陷阱變體

// 變體 1:List 索引陷阱
List<Short> list = new ArrayList<>();
list.add((short)1);
list.remove(1);  // 移除索引 1 而非元素 1!編譯器不報錯

// 變體 2:Map 鍵類型陷阱
Map<Short, String> map = new HashMap<>();
map.put((short)1, "one");
map.get(1);  // 返回 null!Integer(1) != Short(1)

// 變體 3:流操作陷阱
Short[] arr = {1,2,3};
Arrays.stream(arr).filter(x -> x == 1).count();  // OK: x 自動拆箱為 short
IntStream.range(0, 10).filter(i -> arr[i] == 1).count();  // 邏輯錯誤:比較索引與值

總結

這道謎題精準打擊 **Java 類型系統三大痛點**:整數提升規則、自動裝箱語義、泛型擦除後的集合操作不對稱。理解機制、養成強制轉型習慣、編寫類型敏感測試,是避免此類「沉默失效」Bug 的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved