public abstract class Fish { protected String name; public Fish(String name) { this.name = name; } public String getName() { return name; } public abstract void swim(); }每種魚游泳方式可能不一樣,所以swim()為abstract,那class也要abstract了。
然後開始做其他魚,接著定義每個魚的游泳方式:
public class Anemonefish extends Fish { public Anemonefish(String name) { super(name); } @Override public void swim() { System.out.printf("小丑魚 %s 游泳%n", name); } }
public class Sharkfish extends Fish{ public Sharkfish(String name) { super(name); } @Override public void swim() { System.out.printf("鯊魚 %s 游泳%n", name); } }再來,如果我要加個人類進去呢?這樣做嗎?也一樣嗎?但這樣並不符合設計邏輯,讓人類也繼承Fish,不太對...
這時就要用interface介面定義行為:
public interface Swimmer { public abstract void swim(); }現在你人類可以這樣做:
public class Human implements Swimmer { private String name; public Human(String name) { this.name = name; } public String getName() { return name; } @Override public void swim() { System.out.printf("人類 %s 游泳%n", name); } }透過implements關鍵字,讓Human擁有Swimmer這個行為,並且實作swim(),如不實作前面要加abstract。
也讓Fish擁有這個行為,這樣程式會比較有彈性:
public abstract class Fish implements Swimmer { protected String name; public Fish(String name) { this.name = name; } public String getName() { return name; } @Override public abstract void swim(); }
接下來再看一個我們上次在多型中看過的語法結構,只差這次是interface,也有這種結構
Swimmer swimmer1 = new Sharkfish("aaa"); Swimmer swimmer2 = new Human("bbb"); Swimmer swimmer3 = new Submarine("ccc");在多型是用「一種」來解釋,這裡是用擁有某種行為來解釋:
可用Sharkfish是不是擁有Swimmer行為或是Human是不是實作Swimmer介面,而這三行都可以通過編譯,因他們確實都擁有也實作了介面與行為。
再看:
Swimmer swimmer = new Sharkfish();//通過編譯
Sharkfish shark = swimmer;//編譯失敗,因有swimmer行為的物件,不一定就是Sharkfish
我們可以加扮演語法():
Swimmer swimmer1 = new Sharkfish("123");
Sharkfish shark = (Sharkfish) swimmer1;//swimmer1就是Sharkfish的instance,扮演Sharkfish當然通過編譯了。
Swimmer swimmer = new Sharkfish("aaa");
Fish fish = swimmer;//編譯失敗,因為實作Swimmer介面的物件不一定就繼承Fish,像Human就沒繼承。
Swimmer swimmer = new Sharkfish("aaa");//成功
Fish fish = (Fish) swimmer;//編譯成功,你知道有Swimmer行為的物件,不一定繼承Fish,那讓swimmer當 (Fish)就成功了,而執行時swimmer也參考Sharkfish實例。
Swimmer swimmer = new Human("aaa");
Sharkfish shark = (Sharkfish) swimmer;
swimmer參考Human的實例,現在要它當鯊魚當然錯誤了。
執行拋出ClassCastException錯誤
Swimmer swimmer = new Submarine("aaa");
Fish fish = (Fish) swimmer;
swimmer 參考Submarine實例,現在讓swimmer扮演Fish,Submarine也沒繼承Fish,這樣當然錯了。
現在來讓魚游起來:
public class OceansGame { public static void main(String[] args) { Piranhafish piranhafish = new Piranhafish("aaa"); Sharkfish sharkfish = new Sharkfish("bbb"); Human human = new Human("ccc"); doSwim(piranhafish); doSwim(sharkfish); doSwim(human); } public static void doSwim(Fish fish) { fish.swim(); } public static void doSwim(Human human) { human.swim(); } }因為Human沒繼承Fish所以要另外寫一個。 來思考一下,雖然有的沒繼承要另外寫,但是不是可以利用他們都同樣擁有同個行為來做呢?它們都擁有Swimmer行為的特徵,所以可以這樣寫:
public class OceansGame { public static void main(String[] args) { doSwim(new Anemonefish("尼莫")); doSwim(new Sharkfish("蘭尼")); doSwim(new Human("賈斯汀")); doSwim(new Submarine("黃色一號")); } public static void doSwim(Swimmer swimmer) { swimmer.swim(); } }
那...,現在有個海上飛機具有飛行的行為:
public interface Flyer { public abstract void fly(); }
也要能再水上航行:
public class Seaplane implements Swimmer, Flyer { private String name; public Seaplane(String name) { this.name = name; } @Override public void fly() { System.out.printf("海上飛機 %s 在飛%n", name); } @Override public void swim() { System.out.printf("海上飛機 %s 航行海面%n", name); } }Java中,一個類別可實作多個介面。
那如果是飛魚呢?
public class FlyingFish extends Fish implements Flyer { public FlyingFish(String name) { super(name); } @Override public void swim() { System.out.println("飛魚游泳"); } @Override public void fly() { System.out.println("飛魚會飛"); } }同時繼承某個類別,也實作某個介面。例如FlyingFish是一種魚,也擁有Flyer的行為。
現在讓他們動起來:
public class OceansGame { public static void main(String[] args) { doSwim(new Seaplane("空軍零號")); doSwim(new FlyingFish("甚平")); } public static void doSwim(Swimmer swimmer) { swimmer.swim(); } } run: 海上飛機 空軍零號 航行海面 飛魚游泳
現在又有一個需求了!把潛水、游泳分開。
public interface IF_Diver extends Swimmer{ public abstract void dive(); }可以看到介面也可以繼承介面。
設一般的船可以在海面上航行,也就是擁有Swimmer行為:
public class Boat implements Swimmer{ protected String name; public Boat(String name) { this.name = name; } @Override public void swim() { System.out.printf("船在水面 %s 航行%n", name); } }
剛剛的潛水挺,可以潛水也可海上航行:
public class Submarine extends Boat implements IF_Diver{ public Submarine(String name) { super(name); } @Override public void dive() { System.out.printf("潛水艇 %s 潛行%n", name); } }同時繼承類別和實作介面。
裡面的變數宣告:
interface Vehicle { //右轉最大角度 public static final int MAX_TURN_ANGLE = 60; public static final int MAX_TURN_ANGLE = 50; public void turnRight(); }在interface中,也只能定義public static final的列舉常數,所以不寫public static final也可。
以下程式碼會編譯錯誤,因為interface Action裡的method預設public,而重新定義execute()的Some沒標public。
public class Main { public static void main(String[] args) { Action action = new Some(); action.execute(); } } interface Action { void execute(); } class Some implements Action { void execute() { System.out.println("作一些服務"); } }
再提一個介面繼承的例子:
interface Some { void execute(); void doSome(); } interface Other { void execute(); void doOther(); } public class Service implements Some, Other { @Override public void execute() { System.out.println("execute()"); } @Override public void doSome() { System.out.println("doSome()"); } @Override public void doOther() { System.out.println("doOther()"); } }如果Some和Other中的execute()一樣的話,我們就可以用介面繼承改寫:
interface Action { void execute(); } interface Some extends Action { void doSome(); } interface Other extends Action { void doOther(); } public class Service implements Some, Other { @Override public void execute() { out.println("execute()"); } @Override public void doSome() { out.println("doSome()"); } @Override public void doOther() { out.println("doOther()"); } }
結論:
- 一個class可同時實作多個interface,其實就是多重繼承。
- 只定義有哪些行為,但不定義實作內容,所以interface裡method一定是public abstract開頭。就算不寫系統也會幫你寫好。
- 如有一class實作一interface,1.可選擇實作它,也就是重新定義2.method標註abstract。
- 也可以繼承介面。
- 可讓程式更有彈性和維護性高。
- 具有多型。
- 只能宣告常數,不能宣告 instance variable。
參考資料:
良葛格-介面定義行為
良葛格-行為的多型
良葛格-解決需求變化
物件導向軟體工程-介面
沒有留言:
張貼留言