CodeGym /課程 /JAVA 25 SELF /多型與抽象類別的關係

多型與抽象類別的關係

JAVA 25 SELF
等級 18 , 課堂 4
開放

1. 抽象類別與方法

有時在生活(也在程式設計)中,我們會說:「嗯,我不知道它究竟怎麼做,但我確信它應該存在!」例如,所有動物都應該會發出聲音,但發出什麼聲音——取決於具體的動物。針對這類情況,Java 設計了 抽象類別抽象方法

抽象類別 是無法直接被建立的類別(如果 Animal 是抽象的,不能寫 new Animal()),但可以被繼承。這樣的類別可以同時包含一般(已實作)的方法與抽象的方法——也就是已宣告但尚未實作的方法。

抽象方法 是沒有方法本體的方法。它以關鍵字 abstract 宣告,且必須在子類別中實作(除非子類別本身也是抽象的)。

生活化範例

假設我們有一個動物園的應用程式。我們希望所有動物都有一個 makeSound() 方法,但不知道它們各自會發出什麼聲音。那我們就建立一個抽象類別:

public abstract class Animal {
    public abstract void makeSound(); // 抽象方法
}

具體的動物各自以自己的方式實作這個方法:

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("汪汪!");
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("喵!");
    }
}

現在,如果有人試圖建立 new Animal(),編譯器會立刻說:「抱歉,抽象的動物在自然界並不存在!」這很有用:你能保證程式中只會存在具有具體行為的具體動物。

2. 透過抽象實現多型

抽象 是為一組物件抽取出共同介面的過程。抽象類別正是用來定義這個共同介面:它指出所有子類別必須實作哪些方法。

多型與抽象相輔相成:抽象類別保證所有繼承者都有需要的方法,而多型允許你透過基底型別的參考來呼叫這些方法。

範例:建立一座動物園

讓我們組一個小型動物園。我們有抽象類別 Animal 以及它的幾個子類別:

public abstract class Animal {
    public abstract void makeSound();
}
public class Cow extends Animal {
    @Override
    public void makeSound() {
        System.out.println("哞——!");
    }
}
public class Duck extends Animal {
    @Override
    public void makeSound() {
        System.out.println("呱呱!");
    }
}

現在我們可以建立一個動物陣列:

Animal[] zoo = {
    new Dog(),
    new Cat(),
    new Cow(),
    new Duck()
};

for (Animal animal : zoo) {
    animal.makeSound(); // 對每個動物都會呼叫到「正確」的方法
}

陣列中的每個物件都是具體的動物,但對程式碼而言它只是 Animal。仰賴多型與抽象,我們可以確信每個物件都有 makeSound() 方法,而且會正確運作。

3. 使用抽象類別來實現多型

讓我們看一個更實用的例子。假設我們在開發公司員工管理的應用程式。我們有不同類型的員工:經理、開發者、測試人員。大家都有一個共同的方法 work(),但執行方式各不相同。

抽象類別 Employee

public abstract class Employee {
    protected String name;

    public Employee(String name) {
        this.name = name;
    }

    public abstract void work();
}

具體子類別

public class Manager extends Employee {
    public Manager(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " 帶領團隊。");
    }
}

public class Developer extends Employee {
    public Developer(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " 撰寫程式碼。");
    }
}

public class Tester extends Employee {
    public Tester(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " 測試應用程式。");
    }
}

多型的使用

現在我們可以建立一個員工陣列,並為每個員工呼叫 work() 方法:

Employee[] employees = {
    new Manager("Anna"),
    new Developer("Ivan"),
    new Tester("Mariya")
};

for (Employee e : employees) {
    e.work();
}

輸出:

Anna 帶領團隊。
Ivan 撰寫程式碼。
Mariya 測試應用程式。

請注意:在迴圈中我們不知道(也不想知道!)具體的員工型別。我們只呼叫 work(),每個物件就會各司其職。

4. 實用細節

方法實作的保證
抽象類別會強制所有繼承者實作所需的方法。如果你忘了在子類別中實作抽象方法,編譯器立刻會提醒你:「你必須這麼做!」

通用介面
處理抽象型別的陣列或清單(Employee[]List<Animal>)的程式碼可以完全通用。你可以新增新的子類別——而主要程式碼毋須修改。

防止「不合規」的物件
由於抽象類別無法直接建立,沒有人會不小心做出一個「不明所以」且未實作必要方法的物件。

理論與語法:如何宣告抽象類別與方法

  • 抽象類別以關鍵字 abstract 置於 class 前宣告。
  • 抽象方法以關鍵字 abstract 宣告,且沒有方法本體(只以分號結束)。
  • 只要類別內有至少一個抽象方法,該類別就必須是抽象的。
  • 繼承抽象類別的類別必須實作其所有抽象方法,否則它本身也必須是抽象的。

範例結構

public abstract class Animal {
    public abstract void makeSound();
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("喵!");
    }
}

5. 使用抽象類別時的常見錯誤

錯誤 1:嘗試建立抽象類別的物件。
new Animal() 這樣的程式碼無法編譯。抽象類別就像只有組裝說明、沒有零件的家具:在出現具體子類別之前,無法組出物件。

錯誤 2:忘了在子類別實作抽象方法。
如果你宣告了抽象方法,卻沒有在繼承者中實作它(也未將該類別標記為抽象),編譯器會不高興地報錯。

錯誤 3:忽略了存取修飾子。
被覆寫的方法不能有比基底類別更嚴格的存取修飾子。舉例來說,如果抽象方法是 public,那實作也必須是 public(而不是 protectedprivate)。

錯誤 4:試圖讓抽象方法帶有方法本體。
abstract 方法不能有方法本體,否則編譯器會翻白眼說:「請你下定決心——要嘛抽象,要嘛實作!」

錯誤 5:多型不適用於靜態方法。
多型只對非靜態方法有效。靜態方法不會被覆寫——它們是被隱藏,因此呼叫時的行為取決於變數的型別,而非實際物件。

1
問卷/小測驗
多型與多載,等級 18,課堂 4
未開放
多型與多載
多型與多載
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION