讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議6:覆寫變長方法也循規蹈矩 >

建議6:覆寫變長方法也循規蹈矩

在Java中,子類覆寫父類中的方法很常見,這樣做既可以修正Bug也可以提供擴展的業務功能支持,同時還符合開閉原則(Open-Closed Principle),我們來看一下覆寫必須滿足的條件:

重寫方法不能縮小訪問權限。

參數列表必須與被重寫方法相同。

返回類型必須與被重寫方法的相同或是其子類。

重寫方法不能拋出新的異常,或者超出父類範圍的異常,但是可以拋出更少、更有限的異常,或者不拋出異常。

估計你已經猜測出下面要講的內容了,為什麼「參數列表必須與被重寫方法的相同」採用不同的字體,這其中是不是有什麼玄機?是的,還真有那麼一點點小玄機。參數列表相同包括三層意思:參數數量相同、類型相同、順序相同,看上去好像沒什麼問題,那我們來看一個例子,業務場景與上一個建議相同,商品打折,代碼如下:


public class Client{

public static void main(Stringargs){

//向上轉型

Base base=new Sub();

base.fun(100,50);

//不轉型

Sub sub=new Sub();

sub.fun(100,50);

}

}

//基類

class Base{

void fun(int price, int……discounts){

System.out.println("Base……fun");

}

}

//子類,覆寫父類方法

class Sub extends Base{

@Override

void fun(int price, intdiscounts){

System.out.println("Sub……fun");

}

}


請問:該程序有問題嗎?——編譯通不過。那問題出在什麼地方呢?

@Override註解嗎?非也,覆寫是正確的,因為父類的calPrice編譯成字節碼後的形參是一個int類型的形參加上一個int數組類型的形參,子類的參數列表也與此相同,那覆寫是理所當然的了,所以加上@Override註解沒有問題,只是Eclipse會提示這不是一種很好的編碼風格。

難道是"sub.fun(100,50)"這條語句?正解,確實是這條語句報錯,提示找不到fun(int,int)方法。這太奇怪了:子類繼承了父類的所有屬性和方法,甭管是私有的還是公開的訪問權限,同樣的參數、同樣的方法名,通過父類調用沒有任何問題,通過子類調用卻編譯通不過,為啥?難道是沒繼承下來?或者子類縮小了父類方法的前置條件?那如果是這樣,就不應該覆寫,@Override就應該報錯,真是奇妙的事情!

事實上,base對象是把子類對像Sub做了向上轉型,形參列表是由父類決定的,由於是變長參數,在編譯時,"base.fun(100,50)"中的「50」這個實參會被編譯器「猜測」而編譯成「{50}」數組,再由子類Sub執行。我們再來看看直接調用子類的情況,這時編譯器並不會把「50」做類型轉換,因為數組本身也是一個對象,編譯器還沒有聰明到要在兩個沒有繼承關係的類之間做轉換,要知道Java是要求嚴格的類型匹配的,類型不匹配編譯器自然就會拒絕執行,並給予錯誤提示。

這是個特例,覆寫的方法參數列表竟然與父類不相同,這違背了覆寫的定義,並且會引發莫名其妙的錯誤。所以讀者在對變長參數進行覆寫時,如果要使用此類似的方法,請找個小黑屋仔細想想是不是一定要如此。

注意 覆寫的方法參數與父類相同,不僅僅是類型、數量,還包括顯示形式。