整理 Effective Java 書中 Item 53: Use varargs judiciously 心得筆記

主旨

Java 中的 varargs(variable arity arguments,可變參數)允許在方法中接受 不定數量的參數。這在設計彈性 API、像是 printf() 或是工具類別時非常好用。但 varargs 是把雙面刃,用錯了可能導致 執行時錯誤隱性效能問題。這篇將帶你學會:什麼時候該用 varargs,什麼時候該避免,怎麼用才安全又優雅。

範例:設計 min() 函數的錯誤與正確方式

來看看一個錯誤示範:

// ❌ 不建議的寫法
static int min(int... args) {
    if (args.length == 0)
        throw new IllegalArgumentException("Too few arguments");
    int min = args[0];
    for (int i = 1; i < args.length; i++)
        if (args[i] < min)
            min = args[i];
    return min;
}

這段程式碼會在 執行時檢查參數數量是否為 0,這樣不但醜陋,而且不安全,因為錯誤是到了執行階段才會發生。

改進寫法:

// ✅ 建議做法
static int min(int firstArg, int... remainingArgs) {
    int min = firstArg;
    for (int arg : remainingArgs)
        if (arg < min)
            min = arg;
    return min;
}

這種做法能在 編譯階段就避免空參數問題,也讓邏輯更清楚(第一個參數是必要的)。

劃重點

✅ varargs 適用場景:

  • 參數數量不固定,例如 sum(1, 2, 3)printf("Hello %s", name)
  • 搭配一些工具方法或 API wrapper 更有彈性。

⚠️ 使用原則:

  1. 永遠把必要參數放在 varargs 前面
    例如:public static void send(String recipient, String... ccList)

  2. 避免使用空參數造成執行錯誤
    若至少要有一個參數,請像 min() 的寫法那樣處理。

  3. 避免與 overloading 混用產生混淆
    varargs 方法不宜與同參數數量的 method overloading 混用,會增加解析混淆。

  4. 注意效能問題
    每次呼叫 varargs 方法都會建立陣列(即使你只傳一個參數),在高頻率或效能敏感場合要特別小心。

範例:高效能情境下的折衷寫法

假設有 95% 呼叫 foo() 是 0~3 個參數,我們可以這樣設計:

public void foo() { }
public void foo(int a1) { }
public void foo(int a1, int a2) { }
public void foo(int a1, int a2, int a3) { }
public void foo(int a1, int a2, int a3, int... rest) { }

這樣能讓大多數呼叫避開建立陣列的成本,僅在必要時才進入 varargs 版本。

實際應用:EnumSet 的靜態工廠方法

Java 標準庫的 EnumSet.of() 也採用了這種設計模式,針對常見情況提供固定參數版本,在需要時才進入 varargs,確保效能媲美 bit field。

小結

varargs 是強大的語言工具,但使用時要謹慎:

  • ✅ 把必要參數放前面,防止傳入空值
  • ✅ 用在參數數量不確定的工具方法上
  • ⚠️ 避免與 overloading 混用、注意效能開銷
  • 🚫 不應濫用為方便就到處用 varargs

設計彈性好用的 API 不代表可以忽略可讀性與安全性。用 varargs 時多想一步,讓使用API的人少踩雷、少查文件,就是好設計。