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。
參考資料:
良葛格-介面定義行為
良葛格-行為的多型
良葛格-解決需求變化
物件導向軟體工程-介面
沒有留言:
張貼留言