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());
}
}
s.remove(i - 1); // 陷阱在此!
i 類型為 short1 為 int 字面量short - int → **整數提升** → 結果類型為 ints.remove() 參數類型為 Object (非泛型 E)int 自動裝箱為 Integer 對象Set<Short> 內部存儲的是 Short 對象Short 不等於 Integer,即使數值相同,equals() 返回 falsepublic interface Set<E> extends Collection<E> {
boolean add(E e); // 類型安全:必須是 E 或子類型
boolean remove(Object o); // 故意設計為 Object:兼容舊版非泛型代碼
// ...
}
add() 編譯期檢查類型,傳入 Integer 會報錯remove() 接受 Object,**編譯器無法檢測類型錯誤**| 迭代 | 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 未被移除)
byte/short/char 與 int/long 運算自動提升為 int/longint → Integer,short → Short,**不會自動縮窄轉換**add() 嚴格,remove()/contains() 寬鬆 (Object)byte/short 參與運算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 的關鍵。