讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議4:避免帶有變長參數的方法重載 >

建議4:避免帶有變長參數的方法重載

在項目和系統的開發中,為了提高方法的靈活度和可復用性,我們經常要傳遞不確定數量的參數到方法中,在Java 5之前常用的設計技巧就是把形參定義成Collection類型或其子類類型,或者是數組類型,這種方法的缺點就是需要對空參數進行判斷和篩選,比如實參為null值和長度為0的Collection或數組。而Java 5引入變長參數(varags)就是為了更好地提高方法的復用性,讓方法的調用者可以「隨心所欲」地傳遞實參數量,當然變長參數也是要遵循一定規則的,比如變長參數必須是方法中的最後一個參數;一個方法不能定義多個變長參數等,這些基本規則需要牢記,但是即使記住了這些規則,仍然有可能出現錯誤,我們來看如下代碼:


public class Client{

//簡單折扣計算

public void calPrice(int price, int discount){

float knockdownPrice=price*discount/100.0F;

System.out.println("簡單折扣後的價格是:"+formateCurrency(knockdownPrice));

}

//複雜多折扣計算

public void calPrice(int price, int……discounts){

float knockdownPrice=price;

for(int discount:discounts){

knockdownPrice=knockdownPrice*discount/100;

}

System.out.println("複雜折扣後的價格是:"+formateCurrency(knockdownPrice));

}

//格式化成本的貨幣形式

private String formateCurrency(float price){

return NumberFormat.getCurrencyInstance().format(price/100);

}

public static void main(Stringargs){

Client client=new Client();

//499元的貨物,打75折

client.calPrice(49900,75);

}

}


這是一個計算商品價格折扣的模擬類,帶有兩個參數的calPrice方法(該方法的業務邏輯是:提供商品的原價和折扣率,即可獲得商品的折扣價)是一個簡單的折扣計算方法,該方法在實際項目中經常會用到,這是單一的打折方法。而帶有變長參數的calPrice方法則是較複雜的折扣計算方式,多種折扣的疊加運算(模擬類是一種比較簡單的實現)在實際生活中也是經常見到的,比如在大甩賣期間對VIP會員再度進行打折;或者當天是你的生日,再給你打個9折,也就是俗話說的「折上折」。

業務邏輯清楚了,我們來仔細看看這兩個方法,它們是重載嗎?當然是了,重載的定義是「方法名相同,參數類型或數量不同」,很明顯這兩個方法是重載。但是再仔細瞧瞧,這個重載有點特殊:calPrice(int price, int...discounts)的參數範疇覆蓋了calPrice(int price, int discount)的參數範疇。那問題就出來了:對於calPrice(49900,75)這樣的計算,到底該調用哪個方法來處理呢?

我們知道Java編譯器是很聰明的,它在編譯時會根據方法簽名(Method Signature)來確定調用哪個方法,比如calPrice(499900,75,95)這個調用,很明顯75和95會被轉成一個包含兩個元素的數組,並傳遞到calPrice(int price, in..discounts)中,因為只有這一個方法簽名符合該實參類型,這很容易理解。但是我們現在面對的是calPrice(49900,75)調用,這個「75」既可以被編譯成int類型的「75」,也可以被編譯成int數組「{75}」,即只包含一個元素的數組。那到底該調用哪一個方法呢?

我們先運行一下看看結果,運行結果是:

簡單折扣後的價格是:¥374.25。

看來是調用了第一個方法,為什麼會調用第一個方法,而不是第二個變長參數方法呢?因為Java在編譯時,首先會根據實參的數量和類型(這裡是2個實參,都為int類型,注意沒有轉成int數組)來進行處理,也就是查找到calPrice(int price, int discount)方法,而且確認它是否符合方法簽名條件。現在的問題是編譯器為什麼會首先根據2個int類型的實參而不是1個int類型、1個int數組類型的實參來查找方法呢?這是個好問題,也非常好回答:因為int是一個原生數據類型,而數組本身是一個對象,編譯器想要「偷懶」,於是它會從最簡單的開始「猜想」,只要符合編譯條件的即可通過,於是就出現了此問題。

問題是闡述清楚了,為了讓我們的程序能被「人類」看懂,還是慎重考慮變長參數的方法重載吧,否則讓人傷腦筋不說,說不定哪天就陷入這類小陷阱裡了。