1. 程式人生 > >effectiveJava學習筆記:方法(二)

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,再分配一個數組,就沒有任何意義了。