主旨

Java 有兩套型別系統:原始型別(primitive)與其對應的物件型別(boxed primitive)。例如:

  • intInteger
  • doubleDouble
  • booleanBoolean

雖然 autoboxing 看似無縫,但事實上這兩種型別在「效能、null 安全性、== 比較」上都有重大差異。
原則很簡單:如果可以用 primitive,就不要用 boxed。

點出問題:== 比較失準、NullPointerException、效能變慢

錯誤範例一:== 比較 boxed primitive,會比較「物件身份」不是值!

Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);
System.out.println(naturalOrder.compare(new Integer(42), new Integer(42))); // 輸出竟然是 1!

原因是 (i == j) 比較的是兩個 Integer 的物件身份(reference),不是值。正確寫法:

Comparator<Integer> naturalOrder = (iBoxed, jBoxed) -> {
    int i = iBoxed, j = jBoxed;
    return Integer.compare(i, j); // 或 i < j ? -1 : (i == j ? 0 : 1)
};

錯誤範例二:boxed primitive 的預設值是 null,小心 NPE

static Integer i;
public static void main(String[] args) {
    if (i == 42) // 自動 unbox 會造成 NullPointerException!
        System.out.println("Unbelievable");
}

解法很簡單:用 int 取代 Integer,就不會有 null 值。

錯誤範例三:不小心用 boxed 會讓效能變慢

Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
    sum += i; // 每次都 boxing/unboxing,超慢
}

上面程式會慢非常多,只因為 sum 用的是 Long 而非 long。修正如下:

long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
    sum += i;
}

小結:何時該用 boxed?何時避免?

用途說明選擇
效能重要、控制 null 安全✅ 用 primitive
需要放進集合(如 List✅ 用 boxed primitive
泛型的類型參數(如 ThreadLocal<Integer>✅ 用 boxed primitive
反射操作需要物件✅ 用 boxed primitive

劃重點

  • intlong 等 primitive → 無 null、無 GC 負擔、比 boxed 更快
  • IntegerLong 是物件 → 會有 null,也會被 == 誤判為不同值
  • autoboxing 不會解決風險,只是語法糖
  • 避免用 == 比較 boxed primitive
  • 避免讓 boxed primitive 預設值是 null

✅ 實務原則:能用 primitive,就用 primitive。boxed 是不得已才用的備案!