Java 抽象類別 (Abstract Class)

抽象類別是不能被實例化的類別,用來作為其他類別的基礎。

基本語法

使用 abstract 關鍵字:

public abstract class Animal {
    protected String name;
    
    // 抽象方法:沒有實作
    public abstract void makeSound();
    
    // 一般方法:有實作
    public void eat() {
        System.out.println(name + " is eating");
    }
}

抽象方法

抽象方法只有宣告,沒有實作,子類別必須實作:

public abstract class Shape {
    public abstract double area();      // 抽象方法
    public abstract double perimeter(); // 抽象方法
}

public class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}

不能實例化

// Animal animal = new Animal();  // 錯誤!不能實例化抽象類別

Animal dog = new Dog("Buddy");    // OK,可以用子類別

完整範例

// 抽象類別
public abstract class Vehicle {
    protected String brand;
    protected int year;
    
    public Vehicle(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }
    
    // 抽象方法
    public abstract void start();
    public abstract void stop();
    
    // 具體方法
    public void displayInfo() {
        System.out.println(year + " " + brand);
    }
}

// 具體子類別
public class Car extends Vehicle {
    private int doors;
    
    public Car(String brand, int year, int doors) {
        super(brand, year);
        this.doors = doors;
    }
    
    @Override
    public void start() {
        System.out.println("轉動鑰匙,引擎啟動");
    }
    
    @Override
    public void stop() {
        System.out.println("踩煞車,熄火");
    }
}

public class Motorcycle extends Vehicle {
    public Motorcycle(String brand, int year) {
        super(brand, year);
    }
    
    @Override
    public void start() {
        System.out.println("按下啟動按鈕");
    }
    
    @Override
    public void stop() {
        System.out.println("拉煞車,熄火");
    }
}

抽象類別的特點

  • 可以有建構子(但不能直接實例化)
  • 可以有抽象方法和具體方法
  • 可以有實例變數
  • 子類別必須實作所有抽象方法,除非子類別也是抽象類別
public abstract class Animal {
    protected String name;
    
    // 建構子
    public Animal(String name) {
        this.name = name;
    }
    
    // 抽象方法
    public abstract void makeSound();
    
    // 具體方法
    public void sleep() {
        System.out.println(name + " is sleeping");
    }
}

部分實作

子類別如果沒有實作所有抽象方法,也必須是抽象類別:

public abstract class Animal {
    public abstract void eat();
    public abstract void sleep();
}

// 只實作部分方法,也要是抽象類別
public abstract class Pet extends Animal {
    @Override
    public void sleep() {
        System.out.println("Sleeping on the couch");
    }
    // eat() 還是抽象的
}

public class Dog extends Pet {
    @Override
    public void eat() {
        System.out.println("Eating dog food");
    }
}

抽象類別 vs 介面

特性抽象類別介面
方法實作可以有Java 8+ 可以有 default
建構子可以有不能有
實例變數可以有只能有常數
多重繼承不支援支援
存取修飾詞都可以預設 public

更多比較請參考 Java 抽象類別 vs 介面

何時使用抽象類別

  1. 有共同的程式碼:子類別共享相同的實作
  2. 「是一種」關係:Dog is an Animal
  3. 需要非 public 成員:需要 protected 變數或方法
  4. 需要建構子:初始化共同的狀態
// 適合用抽象類別:有共同的實作
public abstract class Employee {
    protected String name;
    protected double baseSalary;
    
    public Employee(String name, double baseSalary) {
        this.name = name;
        this.baseSalary = baseSalary;
    }
    
    // 共同的方法
    public String getName() {
        return name;
    }
    
    // 抽象方法:不同員工計算方式不同
    public abstract double calculateSalary();
}

public class Manager extends Employee {
    private double bonus;
    
    public Manager(String name, double baseSalary, double bonus) {
        super(name, baseSalary);
        this.bonus = bonus;
    }
    
    @Override
    public double calculateSalary() {
        return baseSalary + bonus;
    }
}

模板方法模式

抽象類別常用於模板方法模式:

public abstract class DataProcessor {
    // 模板方法:定義演算法骨架
    public final void process() {
        readData();
        processData();
        writeData();
    }
    
    // 子類別實作
    protected abstract void readData();
    protected abstract void processData();
    protected abstract void writeData();
}

public class CSVProcessor extends DataProcessor {
    @Override
    protected void readData() {
        System.out.println("讀取 CSV");
    }
    
    @Override
    protected void processData() {
        System.out.println("處理 CSV");
    }
    
    @Override
    protected void writeData() {
        System.out.println("寫入 CSV");
    }
}