Java 學習筆記 11 – 類別與物件 Part 4(多型 / 介面)


Posted by vickyh1315 on 2024-05-19

多型(Polymorphism)

Polymorphism 這個單字在生物學的定義,是指一個物種的同一種群中存在兩種或多種明顯不同的表型。且必須同一時間在同一棲息地中出現。

套用在程式語言,則是 Object 類別(物種)的某自定義類別(種群)中,存在多種不同的表型(函式 / 方法)。
也就是說多型的特性,可讓同名程式,根據使用的不同類別,而有不同的行為。

延續 Animal 類別的例子,同樣 sayHi() 方法,Cat 跟 Fish 各自繼承後,卻實作不同邏輯,這就是多型的特性。如此一來,只要確保「子類 is a 父類」,不需要重複在子類定義,也能知道他們做一樣的事。

abstract class Animal {
    abstract void sayHi();
    // code ...
}

public class Cat extends Animal {
    @Override
    void sayHi() {
        System.out.println("Meow");
    }
    // code ...
}

public class Fish extends Animal {
    @Override
    void sayHi() {
        System.out.println("Blub...");
    }
    // code ...
}

介面 / 接口(interface)

不論實體或抽象類別,「繼承」這項行為本身就對特性存在某些限制,前面說到繼承是「is a」的概念,Cat is an Animal、Fish is an Animal,但當我們想寫一個 Robot 類別實作 sayHi(),這時我們讓它繼承 Animal 類別,@Override sayHi(),所以 Robot is an Animal,咦?怎麼怪怪的?

假如僅能繼承,就會出現如此荒謬的結論,應對繼承以外的情境,有 介面(interface) 這個抽象型別,做更彈性的定義。

繼承之於「is a」隸屬的概念,介面強調「has a」擁有什麼樣的行為,用來定義抽象方法,讓類別以 implements(實作)使用,而這個概念會被稱作複合(Composition)。一個類別只能繼承一個類別,但能實作多個介面。

interface soundMaker {
    abstract void sayHi();
}

abstract class Animal {
    // code ...
}

abstract class Machine {
    // code ...
}

public class Cat extends Animal implements soundMaker {
    @Override
    public void sayHi() {
        System.out.println("Meow");
    }
}

public class RobotA implements soundMaker {
    @Override
    public void sayHi() {
        System.out.println("I'm RobotA");
    }
}

public class RobotB extends Machine implements soundMaker {
    @Override
    public void sayHi() {
        System.out.println("I'm RobotB");
    }
}

成員定義與修飾子

節錄翻譯介面在 Java 的定義:

介面是一種參考型別,類似於類別,只能包含方法簽名(就是方法)、常數宣告、巢狀類別(在一個「類別」中定義「另外一個類別」的行為),方法只能有 default 方法跟 static 方法,能被類別 implement 實作或其他介面 extends 繼承,不能被實例化。

可以把介面視為一種特別的抽象類別,定義屬性及方法,但介面用途是拿來「實作」,屬性預設 public static final(常數),方法會預設 public abstract,SE8 後 default 能寫實例方法(解決擴展早期介面方法,導致早期實作了介面的類別將新方法視為實例方法,而編譯錯誤的問題),但通常還是定義抽象方法居多。

interface flyer {

    // 屬性僅能宣告常數,預設 public static final
    String name = "defaultName";

    // public 只能寫 abstract 方法,預設 public abstract
    public abstract void land();

    // SE8 後 default 能寫實例方法
    default void fly() {
        System.out.println("default 可以寫實例方法");
    }

}
// 還原 SE8 前不能使用 default 方法的情形
// 以前定義好的介面,介面放置在檔案 a
interface EarlyInterface {
    // 當時定義好的方法
    public abstract void methodforClassA();
    // 很久以後 classB 要使用才定義
    public abstract void methodforClassB();
}

// 定義介面當時的類別,類別放置在檔案 b
class ClassA {
    @Override
    public void methodforClassA();
}

// 很久以後才定義的 classB,類別放置在檔案 c
class ClassB {
    @Override
    public void methodforClassB();
}

// 如果沒有改寫 methodforClassA(),就會造成編譯問題

抽象類別 v.s 介面

抽象類別跟介面看似都可讓類別重新定義屬性與方法,但應用情境不同:
抽象類別:父類希望將實現細節留給子類,希望確保所有子類都滿足父類的結構
介面:希望兩個系統 / 類別 / 組件相互溝通時

介面可看作抽象方法的一種,與抽象類別本體定義方式相同,差異在內部成員定義方式:

abstract class interface
本體 權限限制 public,不能用 static、final 同 abstract class
內部成員
- 屬性
權限不限制、
可 static、final
僅能宣告常數
(public static final)
內部成員
- 實例方法
權限不限制、
關鍵字可 static、final
權限限 default、
關鍵字預設 static
不可 final
內部成員
- 抽象方法
僅能以 public abstract 修飾 同 abstract class

#java







Related Posts

極簡複習:函式和模組的 Python 程式範例

極簡複習:函式和模組的 Python 程式範例

【Day01】ELK環境建置與介紹

【Day01】ELK環境建置與介紹

React hook form(4) - useFormContext & useFormState & useWatch

React hook form(4) - useFormContext & useFormState & useWatch


Comments