2016/5/4

多型(Polymorphism):重新定義實作@Override

承上篇:OOP:多型(Polymorphism) 以Java為例protected:Java為例

前言:這篇主要是探討Java中重新定義Override,根據參考資料自己做的筆記與心得,如日後有新發現都會陸續增加。


現在再審視一次我們所有的程式碼與專案
rpg Package內含RPG.java
Cast Package內含Magician.java   SwordsMan.java   Role.java

Role.java原始碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package Cast;
public class Role {
    private String name;
    private int level;
    private int blood;
    public void setName(String name) {
        this.name = name;
    }
    public void setLevel(int level) {
        this.level = level;
    }
    public void setBlood(int blood) {
        this.blood = blood;
    }
    public int getBlood() {
        return blood;
    }
    public int getLevel() {
        return level;
    }
    public String getName() {
        return name;
    }
}
SwordsMan.java原始碼:
1
2
3
4
5
6
package Cast;
public class SwordsMan extends Role {
    public void fight() {
        System.out.println("揮劍攻擊");
    }
}
Magician.java原始碼:
1
2
3
4
5
6
7
8
9
package Cast;
public class Magician extends Role{
    public void fight() {
        System.out.println("魔法攻擊");
    }
    public void cure() {
        System.out.println("魔法治療");
    }
}
RPG.java原始碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package rpg;
import Cast.Magician;
import Cast.Role;
import Cast.SwordsMan;
public class RPG {
    public static void main(String[] args) {
        SwordsMan swordsman = new SwordsMan();
        swordsman.setName("Xanxus");
        swordsman.setLevel(55);
        swordsman.setBlood(1200);
        
        Magician magician = new Magician();
        magician.setName("Vortix");
        magician.setLevel(131);
        magician.setBlood(2750);
        drawFight(swordsman);
    }
    public static void drawFight(Role role){
        System.out.println(role.getName());
        //role.fight();//編譯失敗
    }
}
看到第18行的函式,現在我要根據我傳進去的職業,做出不同的攻擊,啊但是role.fight(); 會編譯失敗啊~~
因為Role class中沒有fight(),所以不能編譯,那這時怎麼辦?

這時就要用到@Override這東西,也就是重新定義實作。怎麼重新定義?
1.把子類別的方法拉出來,放到父類別中,並且內容為"空"!!像這樣:
1
2
3
4
5
6
public class Role {
    ...public void fight() {
        // 子類別要重新定義fight()的實際行為
    }
}
2.重新定義子類別:
1
2
3
4
5
6
package Cast;
public class SwordsMan extends Role {
    public void fight() {
        System.out.println("揮劍攻擊");
    }
}
這不是跟原本一樣嗎?是的。它的重新定義指說:
在繼承父類別之後,定義與父類別中相同的方法簽署,但實作內容不同,這稱為重新定義(Override)。
白一點,如果父類別想要擁有使用子類別的method,那只要子類別的method介面一樣,就可以直接在父類別做一個與子類別相同的method但內容為空就好,不用實作,實作靠子類別去做,實作內容看子類別而定。

由現有程式碼來看,Role想要用fight()但它沒有,那就在Role裡做一個fight(),就可以用了,操作只要靠SwordsMan上的方法定義。

現在可以這樣用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package rpg;
import Cast.Magician;
import Cast.Role;
import Cast.SwordsMan;
public class RPG {
    public static void main(String[] args) {
        SwordsMan swordsman = new SwordsMan();
        swordsman.setName("Xanxus");
        swordsman.setLevel(55);
        swordsman.setBlood(1200);
        
        drawFight(swordsman);
    }
    public static void drawFight(Role role){
        System.out.print(role.getName());
        role.fight();
    }
}
run:
Xanxus揮劍攻擊

而通常如果子類別重新定義了父類中的method,可在子類前的method加一行@Override ,這樣會要求編譯器檢查,這個method是不是重新定義了父類中某method,如果沒有,就會錯誤。

像現在我把Role的fight()拿掉,它就錯了。

如果你不想你的方法被重新定義Override,那就在method前加final修飾字,這樣想Override你method的人就會編譯錯誤。
final int methodA{
        //........
}


關鍵字super:透過它,子類別可取得父類別中的方法定義,於呼叫前加上super。
如果原本Role就有toString()
1
2
3
4
5
6
7
public abstract class Role {
    ...public String toString() {
        return String.format("(%s, %d, %d)", this.name, 
                this.level, this.blood);
    }
}
透過super取得父類別定義的方法,串接字串:
1
2
3
4
5
6
7
public class SwordsMan extends Role {
.............................
    @Override
    public String toString() {
        return "劍士 " + super.toString();
    }
}
這裡要注意的是,如果父類別方法是public,那最好子類別取得父類方法的方法也要public,原則是權限只能擴大不能縮小。因為如果父類的方法是private,子類public就會編譯錯誤!

1 則留言:

  1. 如果在Role class 建了可以呼叫figth()的方法,正常是呼叫到了子類別的fight,因為實例是子類別,可是如果想在Role裡面呼叫figth()的方法,那個figth想要是Role的figth(),可以使用super嗎

    回覆刪除