在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是要求嚴格的類型匹配的,類型不匹配編譯器自然就會拒絕執行,並給予錯誤提示。
這是個特例,覆寫的方法參數列表竟然與父類不相同,這違背了覆寫的定義,並且會引發莫名其妙的錯誤。所以讀者在對變長參數進行覆寫時,如果要使用此類似的方法,請找個小黑屋仔細想想是不是一定要如此。
注意 覆寫的方法參數與父類相同,不僅僅是類型、數量,還包括顯示形式。