CodeGym /課程 /JAVA 25 SELF /多型的實務運用

多型的實務運用

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

1. 集合中的多型:為什麼需要它?

先從一個問題開始:「在實際程式裡,到底為什麼需要多型?」

想像一座動物園。你有一個基底類別 Animal,以及一堆子類別:DogCatCowParrot,甚至 Platypus(鴨嘴獸,給喜愛獵奇的人)。每種動物都會發出聲音(makeSound()),但各自有不同的實作。

與其為每種動物建立獨立的陣列,你可以宣告一個型別為 Animal 的陣列或列表,然後把任何動物都丟進去:

Animal[] animals = {
    new Dog(),
    new Cat(),
    new Cow(),
    new Parrot()
};

接著你可以走訪這個陣列,對每個元素呼叫 makeSound()

for (Animal animal : animals) {
    animal.makeSound();
}

魔法!每個物件都知道該發出什麼聲音,你不需要寫一大堆 ifswitch

類比範例

就好像你對一群動物下達「發聲!」的指令,每一隻都會自己決定怎麼做:狗會汪汪叫、貓會喵喵叫,而牛——會哞哞叫。你不用分辨誰是誰——只要呼叫同一個方法。

2. 實作範例:員工的類別階層

來做個更貼近現實(以及未來 IT 工作)的例子。假設我們有一家公司的各種員工:經理、開發者、測試人員。每個人都有 work() 方法,但實作方式不同。

宣告基底類別

public class Employee {
    public void work() {
        System.out.println("員工正在工作...");
    }
}

子類別

public class Manager extends Employee {
    @Override
    public void work() {
        System.out.println("經理正在開會。");
    }
}

public class Developer extends Employee {
    @Override
    public void work() {
        System.out.println("開發者正在寫程式碼。");
    }
}

public class Tester extends Employee {
    @Override
    public void work() {
        System.out.println("測試人員正在尋找錯誤。");
    }
}

以基底型別的陣列/列表來使用

public class CompanyDemo {
    public static void main(String[] args) {
        Employee[] team = {
            new Manager(),
            new Developer(),
            new Tester(),
            new Developer()
        };

        for (Employee e : team) {
            e.work(); // 會針對每個物件呼叫「正確」的方法版本
        }
    }
}

執行結果:

經理正在開會。
開發者正在寫程式碼。
測試人員正在尋找錯誤。
開發者正在寫程式碼。

有什麼好處?

  • 你不用寫一堆像「如果是 Developer 就做某某事」的判斷。
  • 要新增一名新員工(例如 Designer)——只要建立新類別並把它加入陣列即可。
  • 使用員工陣列的那段程式碼完全不用改!

3. 多型的優勢:彈性與可擴充性

假設你的公司出現一種新的員工類型——Designer。你只需要做一件事——建立一個新類別:

public class Designer extends Employee {
    @Override
    public void work() {
        System.out.println("設計師正在繪製設計稿。");
    }
}

現在就能把設計師加入團隊:

Employee[] team = {
    new Manager(),
    new Developer(),
    new Tester(),
    new Designer()
};

搞定!程式立刻能正確處理新的員工型別,而無需更改任何一行負責遍歷陣列並呼叫 work() 的程式碼。

這就是可擴充性:你的程式能輕鬆適應新的物件型別。

4. 多型的限制:另一面

可惜任何魔法都有其限制(而且就像任何 RPG 一樣有代價)。

只能使用基底類別中宣告的方法

當你使用型別為 Employee 的變數時,只能呼叫在 Employee 類別中宣告的方法。若 Developer 有專用方法 writeCode(),你無法直接呼叫:

Employee e = new Developer();
// e.writeCode(); // 編譯錯誤:Employee 沒有這個方法!

若真的要呼叫特定方法,就得進行型別轉換。不過這是最後手段。如果你經常需要轉型,或許應該重新檢視類別設計——基底類別或介面應該包含所需的方法。

if (e instanceof Developer) {
    Developer dev = (Developer) e;
    dev.writeCode();
}

但這樣就失去了為之努力的通用性與優雅。因此請盡量把基底類別設計成只包含所有子類別確實需要的方法。

5. 實作:建立員工階層

來點實作:寫個簡單的應用程式,包含幾種員工型別,並用多型來處理它們。

步驟 1:基底類別與子類別

// Employee.java
public class Employee {
    public void work() {
        System.out.println("員工正在工作...");
    }
}

// Manager.java
public class Manager extends Employee {
    @Override
    public void work() {
        System.out.println("經理正在開會。");
    }
}

// Developer.java
public class Developer extends Employee {
    @Override
    public void work() {
        System.out.println("開發者正在寫程式碼。");
    }
}

// Tester.java
public class Tester extends Employee {
    @Override
    public void work() {
        System.out.println("測試人員正在尋找錯誤。");
    }
}

步驟 2:主程式類別

// CompanyDemo.java
public class CompanyDemo {
    public static void main(String[] args) {
        Employee[] team = {
            new Manager(),
            new Developer(),
            new Tester(),
            new Developer()
        };

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

步驟 3:加入可擴充性

假設一個月後公司多了新員工——設計師。你需要做的只有:

public class Designer extends Employee {
    @Override
    public void work() {
        System.out.println("設計師正在繪製設計稿。");
    }
}

好了,現在把設計師加入團隊:

Employee[] team = {
    new Manager(),
    new Developer(),
    new Tester(),
    new Designer()
};

總結

所有核心程式碼(CompanyDemo)都不必改!這就是多型的威力。

6. 使用多型時的常見錯誤

錯誤 1:期待能透過基底型別的參考呼叫子類別的專用方法。
很多新手會嘗試透過超類別型別的變數呼叫子類別的專用方法。比如:

Employee e = new Developer();
// e.writeCode(); // 錯誤!這個方法沒有在 Employee 中定義。

要呼叫專用方法必須轉型,但這會失去通用性。

錯誤 2:沒有使用註解 @Override
若忘了註解,可能不小心寫成新方法(例如拼錯名稱),那多型就不會生效,會被呼叫的是超類別中的版本。

錯誤 3:缺少共同的介面。
如果基底類別沒有包含所需的方法,多型就無法實現。比如,如果在 Employee 中沒有 work() 方法,那迴圈就沒辦法對整個員工陣列都呼叫它。

錯誤 4:違反開放/封閉原則。
如果為了加入新的員工型別而不得不修改負責遍歷陣列/列表的程式碼——代表你沒有正確運用多型。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION