現在有個需求,請設計static
方法,可以播放角色攻擊動畫,你也許會這麼想,學剛剛多型的寫法,設計個drawFight()
方法如何?
對
drawFight()
方法而言,只知道傳進來的會是一種Role
物件,所以編譯器也只能檢查你呼叫的方法,Role
是不是有定義,顯然地,Role
目前並沒有定義fight()
方法,因此編譯錯誤了。然而你仔細觀察一下
SwordsMan
與Magician
的fight()
方法,他們的方法簽署(method signature)都是:public void fight()
也就是說,操作介面是相同的,只是方法實作內容不同,你可以將
fight()
方法提昇至Role
類別中定義:package cc.openhome;
public class Role {
...略
public void fight() {
// 子類別要重新定義fight()的實際行為
}
}
在Role
類別中定義了fight()
方法,由於實際上角色如何攻擊,只有子類別才知道,所以這邊的fight()
方法內容是空的,沒有任何程式碼實作。SwordsMan
繼承Role
之後,再對fight()
的實作進行定義:
package cc.openhome;
public class SwordsMan extends Role {
public void fight() {
System.out.println("揮劍攻擊");
}
}
在繼承父類別之後,定義與父類別中相同的方法簽署,但實作內容不同,這稱為重新定義(Override),因為對父類別中已定義的方法實作不滿意,所以你在子類別中重新定義實作。Magician
繼承Role
之後,也重新定義了fight()
的行為:
package cc.openhome;
public class Magician extends Role {
public void fight() {
System.out.println("魔法攻擊");
}
...略
}
由於Role
現在定義了fight()
方法(雖然方法區塊中沒有程式碼實作),所以編譯器不會找不到Role
的fight()
了,因此你可以如下撰寫:
package cc.openhome;
public class RPG {
public static void main(String[] args) {
SwordsMan swordsMan = new SwordsMan();
swordsMan.setName("Justin");
swordsMan.setLevel(1);
swordsMan.setBlood(200);
Magician magician = new Magician();
magician.setName("Monica");
magician.setLevel(1);
magician.setBlood(100);
drawFight(swordsMan);
drawFight(magician);
}
static void drawFight(Role role) {
System.out.print(role.getName());
role.fight();
}
}
在fight()
方法宣告了Role
型態的參數,那方法中呼叫的,到底是Role
中定義的fight()
,還是個別子類別中定義的fight()
呢?如果傳入fight()
的是SwordsMan
,role
參數參考的就是SwordsMan
實例,操作的就是SwordsMan
上的方法定義。這就好比role
牌子掛在SwordsMan
實例身上,你要求有role
牌子的物件攻擊,發動攻擊的物件就是SwordsMan
實例。同樣地,如果傳入fight()
的是Magician
,role
參數參考的就是Magician
實例,操作的就是Magician
上的方法定義。
所以範例最後的執行結果是:
Justin揮劍攻擊
Monica魔法攻擊
Monica魔法攻擊
在重新定義父類別中某個方法時,子類別必須撰寫與父類別方法中相同的簽署,然而如果疏忽打錯字了:
public class SwordsMan extends Role {
public void Fight() {
System.out.println("揮劍攻擊");
}
}
以這邊的例子來說,父類別中定義的是
fight()
,但子類別中定義了Fight()
,這就不是重新定義fight()
了,而是子類別新定義了一個Fight()
方法,這是合法的方法定義,編譯器並不會發出任何錯誤訊息,你只會在運行範例時,發現為什麼SwordsMan
完全沒有攻擊。在JDK5之後支援標註(Annotation),其中一個內建的標準標註就是
@Override
,如果你在子類別中某個方法前標註@Override
,表示要求編譯器檢查,該方法是不是真的重新定義了父類別中某個方法,如果不是的話,就會引發編譯錯誤。例如: