天天看點

Effective Java 第三版——53. 明智而審慎地使用可變參數

Tips

書中的源代碼位址:https://github.com/jbloch/effective-java-3e-source-code

注意,書中的有些代碼裡方法是基于Java 9 API中的,是以JDK 最好下載下傳 JDK 9以上的版本。

53. 明智而審慎地使用可變參數

可變參數方法正式名稱稱為可變的參數數量方法『variable arity methods』 [JLS, 8.4.1],接受零個或多個指定類型的參數。 可變參數機制首先建立一個數組,其大小是在調用位置傳遞的參數數量,然後将參數值放入數組中,最後将數組傳遞給方法。

例如,這裡有一個可變參數方法,它接受一系列int類型的參數并傳回它們的總和。如你所料,

sum(1,2,3)

的值為6,

sum()

的值為0:

// Simple use of varargs

static int sum(int... args) {
    int sum = 0;
    for (int arg : args)
        sum += arg;
    return sum;
}
           

有時,編寫一個需要某種類型的一個或多個參數的方法是合适的,而不是零或更多。 例如,假設要編寫一個計算其多個參數最小值的方法。 如果用戶端不傳遞任何參數,則此方法定義不明确。 你可以在運作時檢查數組長度:

// The WRONG way to use varargs to pass one or more arguments!

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;
}
           

該解決方案存在幾個問題。 最嚴重的是,如果用戶端在沒有參數的情況下調用此方法,則它在運作時而不是在編譯時失敗。 另一個問題是它很難看。 必須在args參數上包含顯式有效性檢查,除非将

min

初始化為

Integer.MAX_VALUE

,否則不能使用for-each循環,這也很難看。

幸運的是,有一種更好的方法可以達到預期的效果。 聲明方法采用兩個參數,一個指定類型的普通參數,另一個此類型的可變參數。 該解決方案糾正了前一個示例的所有缺陷:

// The right way to use varargs to pass one or more arguments

static int min(int firstArg, int... remainingArgs) {
    int min = firstArg;
    for (int arg : remainingArgs)
        if (arg < min)
            min = arg;
    return min;
}
           

從這個例子中可以看出,在需要參數數量可變的方法時,可變參數是有效的。可變參數是為

printf

方法而設計的,該方法與可變參數同時添加到Java 平台中,以及包括經過改造的核心反射機制。

printf

和反射機制都從可變參數中受益匪淺。

在性能關鍵的情況下使用可變參數時要小心。每次調用可變參數方法都會導緻數組配置設定和初始化。如果你從經驗上确定負擔不起這個成本,但是還需要可變參數的靈活性,那麼有一種模式可以讓你魚與熊掌兼得。假設你已确定95%的調用是三個或更少的參數的方法,那麼聲明該方法的五個重載。每個重載方法包含0到3個普通參數,當參數數量超過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) { }
           

現在你知道,在所有參數數量超過3個的方法調用中,隻有5%的調用需要支付建立數組的成本。與大多數性能優化一樣,這種技術通常不太合适,但一旦真正需要的時候,它是一個救星。

EnumSet

的靜态工廠使用這種技術将建立枚舉集合的成本降到最低。這是适當的,因為枚舉集合為比特屬性提供具有性能競争力的替換(performance-competitive replacement for bit fields)是至關重要的(條目 36)。

總之,當需要使用可變數量的參數定義方法時,可變參數非常有用。 在使用可變參數前加上任何必需的參數,并注意使用可變參數的性能後果。