Java 多型 (Polymorphism)
多型是指同一個方法在不同物件上有不同的行為,是物件導向的核心特性之一。
基本概念
父類別的參考可以指向子類別的物件:
Animal myDog = new Dog(); // 父類別參考指向子類別物件
Animal myCat = new Cat();
myDog.makeSound(); // Woof!(執行 Dog 的版本)
myCat.makeSound(); // Meow!(執行 Cat 的版本)
完整範例
// 父類別
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void makeSound() {
System.out.println("Some sound");
}
}
// 子類別
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " says: Woof!");
}
}
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " says: Meow!");
}
}
// 使用多型
public class Main {
public static void main(String[] args) {
Animal[] animals = {
new Dog("Buddy"),
new Cat("Whiskers"),
new Dog("Max")
};
// 多型:同一方法,不同行為
for (Animal animal : animals) {
animal.makeSound();
}
}
}
// 輸出:
// Buddy says: Woof!
// Whiskers says: Meow!
// Max says: Woof!
多型的好處
1. 統一處理不同型別
public void feedAnimals(Animal[] animals) {
for (Animal animal : animals) {
animal.eat(); // 不需要知道具體型別
}
}
2. 程式碼擴展性
新增子類別不需要修改現有程式碼:
// 新增 Bird 類別
public class Bird extends Animal {
@Override
public void makeSound() {
System.out.println(name + " says: Tweet!");
}
}
// 不需要修改原有的迴圈程式碼
3. 減少重複程式碼
// 不用多型
public void processShape(Object shape) {
if (shape instanceof Circle) {
((Circle) shape).draw();
} else if (shape instanceof Rectangle) {
((Rectangle) shape).draw();
}
// 每新增一種圖形都要加 if
}
// 使用多型
public void processShape(Shape shape) {
shape.draw(); // 自動呼叫正確的版本
}
向上轉型 (Upcasting)
子類別轉為父類別,自動進行:
Dog dog = new Dog("Buddy");
Animal animal = dog; // 向上轉型(自動)
animal.makeSound(); // Woof!(仍執行 Dog 的版本)
// animal.bark(); // 錯誤!Animal 沒有 bark()
向下轉型 (Downcasting)
父類別轉為子類別,需要強制轉型:
Animal animal = new Dog("Buddy");
// 需要強制轉型
Dog dog = (Dog) animal;
dog.bark(); // OK
// 錯誤的轉型會拋出 ClassCastException
Animal cat = new Cat("Whiskers");
// Dog wrongDog = (Dog) cat; // ClassCastException!
安全的向下轉型
Animal animal = getAnimal();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
// Java 16+ 模式匹配
if (animal instanceof Dog dog) {
dog.bark(); // 自動轉型
}
方法覆寫規則
子類別覆寫方法時:
- 方法簽章必須相同
- 存取權限不能更嚴格
- 回傳型別可以是子型別(協變回傳)
public class Animal {
protected Animal getParent() {
return null;
}
}
public class Dog extends Animal {
@Override
protected Dog getParent() { // 協變回傳
return null;
}
}
更多說明請參考 Java 方法覆寫。
實際應用
支付系統
public abstract class Payment {
public abstract void pay(double amount);
}
public class CreditCard extends Payment {
@Override
public void pay(double amount) {
System.out.println("信用卡支付 $" + amount);
}
}
public class PayPal extends Payment {
@Override
public void pay(double amount) {
System.out.println("PayPal 支付 $" + amount);
}
}
// 使用
public void checkout(Payment payment, double amount) {
payment.pay(amount); // 多型
}
checkout(new CreditCard(), 100); // 信用卡支付 $100
checkout(new PayPal(), 100); // PayPal 支付 $100
圖形繪製
public abstract class Shape {
public abstract double area();
public abstract void draw();
}
public class Circle extends Shape {
private double radius;
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public void draw() {
System.out.println("Drawing circle");
}
}
// 統一處理所有圖形
public void processShapes(Shape[] shapes) {
for (Shape shape : shapes) {
shape.draw();
System.out.println("Area: " + shape.area());
}
}
多型的類型
| 類型 | 說明 |
|---|---|
| 執行時期多型 | 方法覆寫(本篇主題) |
| 編譯時期多型 | 方法多載 |