建議4: 避免帶有變長參數的方法重載
在項目和系統的開發中,為了提高方法的靈活度和可復用性,我們經常要傳遞不確定數量的參數到方法中,在Java 5之前常用的設計技巧就是把形參定義成Collection類型或其子類類型,或者是數組類型,這種方法的缺點就是需要對空參數進行判斷和篩選,比如實參為null值和長度為0的Collection或數組。
而 Java 5引入變長參數(varags)就是為了更好地提高方法的復用性,讓方法的調用者可以“隨心所欲”地傳遞實參數量,當然變長參數也是要遵循一定規則的,
比如變長參數必須是方法中的最后一個參數;一個方法不能定義多個變長參數等,這些基本規則需要牢記,但是即使記住了這些規則,仍然有可能出現錯誤,我們來看如下代碼:
1 public class Client { 2 //簡單折扣計算 3 public void calPrice(int price,int discount){ 4 float knockdownPrice =price * discount / 100.0F; 5 System.out.println("簡單折扣后的價格是:"+formateCurrency(knockdownPrice)); 6 } 7 //復雜多折扣計算 8 public void calPrice(int price,int... discounts){ 9 float knockdownPrice = price; 10 for(int discount:discounts){ 11 knockdownPriceknockdownPrice = knockdownPrice * discount / 100; 12 } 13 System.out.println("復雜折扣后的價格是:" +formateCurrency(knockdownPrice)); 14 } 15 //格式化成本的貨幣形式 16 private String formateCurrency(float price){ 17 return NumberFormat.getCurrencyInstance().format(price/100); 18 } 19 20 public static void main(String[] args) { 21 Client client = new Client(); 22 //499元的貨物,打75折 23 client.calPrice(49900, 75); 24 } 25 }
運行結果:
簡單折扣后的價格是:¥374.25
這是一個計算商品價格折扣的模擬類,帶有變長參數的calPrice方法則是較復雜的折扣計算方式,多種折扣的疊加運算(模擬類是一種比較簡單的實現)在實際生活中也是經常見到的,比如在大甩賣期間對VIP會員再度進行打折;或者當天是你的生日,再給你打個9折,也就是俗話說的“折上折”。
兩個calPrice()方法重載有點特殊:calPrice(int price,int... discounts)的參數范疇覆蓋了calPrice(int price,int discount)的參數范疇。那問題就出來了:對於calPrice(49900,75)這樣的計算,到底該調用哪個方法來處理呢?
從運行結果來看是調用了第一個方法,而不是變長參數的方法.
因為Java在編譯時,首先會根據實參的數量和類型(這里是2個實參,都為int類型,注意沒有轉成int數組)來進行處理,也就是查找到calPrice(int price,int discount)方法,而且確認它是否符合方法簽名條件。現在的問題是編譯器為什么會首先根據2個int類型的實參而不是1個int類型、1個int數組類型的實參來查找方法呢?這是個好問題,也非常好回答:因為int是一個原生數據類型,而數組本身是一個對象,編譯器想要“偷懶”,於是它會從最簡單的開始“猜想”,只要符合編譯條件的即可通過,於是就出現了此問題。
問題是闡述清楚了,為了讓我們的程序能被“人類”看懂,還是慎重考慮變長參數的方法重載吧,否則讓人傷腦筋不說,說不定哪天就陷入這類小陷阱里了。
//============================================
這里說一個Java編譯器的"最短路徑原則":如果能夠在本類中查找到的變量,常量,方法,就不會到其他包或父類,接口中查找,以確保本類中的屬性方法優先.