effectiveJava學習筆記:方法(二)
慎用過載
在Java中,同一個類中的多個方法可以有相同的方法名稱,但是有不同的引數列表,這就稱為方法過載(method overloading)。
引數列表又叫引數簽名,包括引數的型別、引數的個數、引數的順序,只要有一個不同就叫做引數列表不同。
如下面的例子:
public class Demo { //一個普通得方法,不帶引數,無返回值 public void add(){ //method body } //過載上面的方法,並且帶了一個整形引數,無返回值 public void add(int a){ //method body } //過載上面的方法,並且帶了兩個整型引數,返回值為int型 public int add(int a,int b){ //method body return 0; } }
方法的過載的規則:
- 方法名稱必須相同。
- 引數列表必須不同。
- 方法的返回型別可以相同也可以不相同。
- 僅僅返回型別不同不足以稱為方法的過載
但是,我們應該避免胡亂使用過載機制
過載容易產生的問題:過載是根據引數的靜態型別選擇執行方法,而方法重寫是根據引數的動態型別選擇執行方法。
例如People p = new Man();那麼People是靜態型別,Man是動態型別。
覆蓋機制很容易讓期望落空。因為如果不知道過載是根據引數的靜態型別選擇執行方法,那麼覆蓋就不能執行期待執行的方法。
public class CollectionClassifier { public static String classify(Set < ? > s) { return "Set"; } public static String classify(List < ? > lst) { return "List"; } public static String classify(Collection < ? > c) { return "Unknown Collection"; } public static void main(String[] args) { Collection < ? >[] collections = {new HashSet < String >(),new ArrayList < BigInteger >(),new HashMap < String,String >().values()}; for(Collection < ? > c:collections) System.out.println(classify(c)); } }
結果出現的是三行一樣的答案Unknown Collection,這就是因為過載是靜態型別選擇方法。
慎用可變引數
可變引數的機制是通過先建立一個數組,陣列的大小為在呼叫位置所傳遞的引數數量,然後將引數值傳到陣列中,最後將陣列傳遞給方法
package com.ligz.Chapter7.Item42; /** * @author ligz */ public class Main { static int sum(int... args) { int sum=0; for(int arg : args) sum += arg; return sum; } public static void main(String[] args) { int[] i = {1,2,3}; System.out.println(sum(i)); } }
但是,不傳參也是可以的,這樣容易導致錯誤的出現,所以常見的策略是首先指定正常引數,把可選引數放在後面。
//可變引數必須放在引數列表的最後
static int min(int firstArg, int... remainingArgs) {
int min = firstArg;
for(int arg : remainingArgs)
if(arg < min)
min = arg;
return min;
}
在重視效能的情況下,使用可變引數機制要小心,因為可變引數方法的每次呼叫都會導致進行一次陣列分配和初始化,有一種折中的解決方案,假設確定某個方法大部分呼叫會有3個或者更少的引數,就宣告該方法的5個過載,每個過載帶有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}
返回零長度的陣列或者集合,而不是null
有人覺得,null的返回值比零長度陣列更好,因為它避免了分配陣列所需要的開銷,顯然這種說法是站不住腳的:
對於這個問題,邏輯出錯比效能下降造成的後果更嚴重,除非有足夠多的證據證明確實是在這裡造成的效能問題;
零長度的陣列,其實並不比null佔用太多的額外開銷;
如果真的返回次數太多,其實我們可以使用同一個零長度的陣列。
返回空:
private final List<Cheese> cheesesInStock=new ArrayList<>();
public Cheese[] getCheeses()
{
if(cheesesInStock.size()==0)
{
return null;
}
....
}
陣列返回0長度:
private final List<Cheese> cheesesInStock = new ArrayList<>();
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses() {
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
對於集合返回0長度:
public List<Cheese> getCheeseList() {
if (cheesesInStock.isEmpty()) {
return Collections.emptyList();
} else {
return cheesesInStock;
}
}
在Collections中有專門針對List,Set,Map的空的實現。如:
Collections.emptyList()
Collections.emptySet();
Collections.emptyMap();
總之,返回型別為陣列或者集合的方法,沒有理由返回null,而是返回一個零長度的陣列或者集合。這種習慣的做法(返回null)可能來自於C,因為C中,陣列和陣列的長度是分開計算的( sizeof(陣列名)/sizeof(陣列名[0])),如果返回的陣列長度為0,再分配一個數組,就沒有任何意義了。