繼承基本上就是避免多個類別間重複定義了相同的實作。以實際的例子來說明比較清楚,假設你在正開發一款RPG(Role-playing game)遊戲,一開始設定的角色有劍士與魔法師。首先你定義了劍士類別:
public class SwordsMan {
private String name; // 角色名稱
private int level; // 角色等級
private int blood; // 角色血量
public void fight() {
System.out.println("揮劍攻擊");
}
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接著你為魔法師定義類別:
public class Magician {
private String name; // 角色名稱
private int level; // 角色等級
private int blood; // 角色血量
public void fight() {
System.out.println("魔法攻擊");
}
public void cure() {
System.out.println("魔法治療");
}
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
你注意到什麼呢?因為只要是遊戲中的角色,都會具有角色名稱、等級與血量,類別中也都為名稱、等級與血量定義了取值方法與設值方法,Magician中粗體字部份與SwordsMan中相對應的程式碼重複了。重複在程式設計上,就是不好的訊號。舉個例子來說,如果你要將name、level、blood改名為其他名稱,那就要修改SwordsMan與Magician兩個類別,如果有更多類別具有重複的程式碼,那就要修改更多類別,造成維護上的不便。
如果要改進,可以把相同的程式碼提昇(Pull up)為父類別:
package cc.openhome;
public class Role {
private String name;
private int level;
private int blood;
public int getBlood() {
return blood;
}
public void setBlood(int blood) {
this.blood = blood;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
這個類別在定義上沒什麼特別的新語法,只不過是將SwordsMan與Magician中重複的程式碼複製過來。接著SwordsMan可以如下繼承Role:
package cc.openhome;
public class SwordsMan extends Role {
public void fight() {
System.out.println("揮劍攻擊");
}
}
在這邊看到了新的關鍵字extends,這表示SwordsMan會擴充Role的實作,也就是繼承Role的實作,再擴充Role原本沒有的fight()實作。程式面上來說,Role中有定義的程式碼,SwordsMan中都繼承而擁有了,並再定義了fight()方法的程式碼。類似地,Magician也可以如下定義繼承Role類別:
package cc.openhome;
public class Magician extends Role {
public void fight() {
System.out.println("魔法攻擊");
}
public void cure() {
System.out.println("魔法治療");
}
}
Magician繼承Role的實作,再擴充了Role原本沒有的fight()與cure()實作。
如何看出確實有繼承了呢?以下簡單的程式可以看出:
package cc.openhome;
public class RPG {
public static void main(String[] args) {
demoSwordsMan();
demoMagician();
}
static void demoSwordsMan() {
SwordsMan swordsMan = new SwordsMan();
swordsMan.setName("Justin");
swordsMan.setLevel(1);
swordsMan.setBlood(200);
System.out.printf("劍士:(%s, %d, %d)%n", swordsMan.getName(),
swordsMan.getLevel(), swordsMan.getBlood());
}
static void demoMagician() {
Magician magician = new Magician();
magician.setName("Monica");
magician.setLevel(1);
magician.setBlood(100);
System.out.printf("魔法師:(%s, %d, %d)%n", magician.getName(),
magician.getLevel(), magician.getBlood());
}
}
雖然SwordsMan與Magician並沒有定義getName()、getLevel()與getBlood()等方法,但從Role繼承了這些方法,所以就如範例中可以直接使用,執行的結果如下:
魔法師:(Monica, 1, 100)
繼承的好處之一,就是若你要將
name、level、blood改名為其它名稱,那就只要修改Role.java就可以了,只要是繼承Role的子類別都無需修改。有的書籍或文件會說,private成員無法繼承,那是錯的!如果private成員無法繼承,那為什麼上面的範例name、level、blood記錄的值會顯示出來呢?private成員會被繼承,只不過子類別無法直接存取,必須透過父類別提供的方法來存取(如果父類別願意提供存取方法的話)。

