Java 繼承 (Inheritance)

繼承讓子類別可以重複使用父類別的程式碼,建立類別之間的階層關係。

基本語法

使用 extends 關鍵字:

// 父類別
public class Animal {
    protected String name;
    
    public void eat() {
        System.out.println(name + " is eating");
    }
}

// 子類別
public class Dog extends Animal {
    public void bark() {
        System.out.println(name + " says: Woof!");
    }
}

// 使用
Dog dog = new Dog();
dog.name = "Buddy";
dog.eat();   // 繼承自 Animal
dog.bark();  // Dog 自己的方法

繼承的特點

  • 子類別繼承父類別的所有非私有成員
  • Java 只支援單一繼承(一個類別只能有一個父類別)
  • 所有類別都繼承自 Object
public class Animal { }
public class Dog extends Animal { }
public class Cat extends Animal { }

// 錯誤:不能多重繼承
// public class DogCat extends Dog, Cat { }

super 關鍵字

呼叫父類別建構子

public class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
}

public class Dog extends Animal {
    private String breed;
    
    public Dog(String name, String breed) {
        super(name);  // 呼叫父類別建構子
        this.breed = breed;
    }
}

呼叫父類別方法

public class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

public class Dog extends Animal {
    @Override
    public void makeSound() {
        super.makeSound();  // 呼叫父類別方法
        System.out.println("Woof!");
    }
}

更多說明請參考 Java super 關鍵字

方法覆寫 (Override)

子類別可以重新定義父類別的方法:

public class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

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

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }
}
建議使用 @Override 註解,編譯器會檢查是否正確覆寫。

更多說明請參考 Java 方法覆寫

繼承與建構子

子類別建構子會先呼叫父類別建構子:

public class Animal {
    public Animal() {
        System.out.println("Animal 建構子");
    }
}

public class Dog extends Animal {
    public Dog() {
        // super() 會自動呼叫
        System.out.println("Dog 建構子");
    }
}

new Dog();
// 輸出:
// Animal 建構子
// Dog 建構子

如果父類別沒有無參數建構子,必須明確呼叫 super()

public class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);  // 必須呼叫
    }
}

實際範例

// 父類別
public class Vehicle {
    protected String brand;
    protected int year;
    
    public Vehicle(String brand, int year) {
        this.brand = brand;
        this.year = year;
    }
    
    public void start() {
        System.out.println(brand + " is starting");
    }
    
    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 displayInfo() {
        super.displayInfo();
        System.out.println("Doors: " + doors);
    }
    
    public void honk() {
        System.out.println("Beep!");
    }
}

// 使用
Car myCar = new Car("Toyota", 2024, 4);
myCar.start();        // 繼承
myCar.displayInfo();  // 覆寫
myCar.honk();         // Car 專有

繼承的存取權限

父類別修飾詞子類別可存取
public
protected
default同套件才可以
private

instanceof

檢查物件是否屬於某類別:

Dog dog = new Dog("Buddy");

System.out.println(dog instanceof Dog);     // true
System.out.println(dog instanceof Animal);  // true
System.out.println(dog instanceof Object);  // true

Animal animal = new Animal("Generic");
System.out.println(animal instanceof Dog);  // false

何時使用繼承

使用繼承的情況:

  • 「是一種」(is-a) 關係:Dog is an Animal
  • 需要重複使用父類別的程式碼
  • 需要多型行為

避免繼承的情況:

  • 「有一個」(has-a) 關係:Car has an Engine(使用組合)
  • 只是想重用程式碼(考慮組合或委派)
// 繼承:Dog is an Animal
class Dog extends Animal { }

// 組合:Car has an Engine
class Car {
    private Engine engine;  // 組合
}