1. 抽象類別:複習基礎
在開始比較之前,先複習一下什麼是抽象類別。
抽象類別——是一種無法直接被建立的類別(不能寫 new Animal()),但它可以同時包含一般方法(有實作)與抽象方法(無實作)。抽象類別常被用作其他類別的基底,子類別會繼承其行為,並且必須實作部分方法。
例如,在應用程式中如果有不同種類的交通工具,可以建立抽象類別 Transport:
public abstract class Transport {
private String model;
public Transport(String model) {
this.model = model;
}
public String getModel() {
return model;
}
// 抽象方法 — 沒有實作,只有宣告
public abstract void move();
// 一般方法 — 有實作
public void printInfo() {
System.out.println("交通工具型號: " + model);
}
}
抽象類別的特性:
- 可以包含欄位(狀態)。
- 可以包含已實作的方法。
- 可以包含抽象方法(子類別必須實作)。
- 不可直接建立實例。
- 用於提供共通行為與狀態。
2. 介面:複習基礎
介面是一組類別必須實作的方法。介面不描述狀態(不能有一般欄位,只能有常數),而且在 Java 8 之前不包含方法實作。介面是純粹的合約: 「如果你實作我,你就必須能做這些事」。
介面範例:
public interface Movable {
void move(int x, int y);
}
介面的特性:
- 不包含狀態(只允許 public static final 常數)。
- 在 Java 8 之前——只有抽象方法(自 Java 8 起有 default 與 static 方法,稍後再談)。
- 方法預設皆為 public abstract。
- 類別可以實作多個介面。
- 用於描述能力,類別「會做什麼」。
3. 比較表:抽象類別 vs 介面
是時候把這兩個工具放在一起比較了!以下是直觀的表格:
| 特性 | 抽象類別 | 介面 |
|---|---|---|
| 語法 | |
|
| 可以建立實例嗎? | 否 | 否 |
| 可包含一般方法嗎? | 是 | 在 Java 8 之前——否;自 Java 8 起——僅限 default/static |
| 可包含抽象方法嗎? | 是 | 是(Java 8 之前所有方法皆為抽象) |
| 可包含欄位(狀態)嗎? | 是(任何欄位) | 僅限 public static final(常數) |
| 可包含建構子嗎? | 是 | 否 |
| 繼承 | 只能有一個抽象/一般父類別 | 可以實作多個介面 |
| 方法的修飾子 | 任意(public、protected、private) | 方法預設為 public abstract。自 Java 9 起可在介面內新增 private 方法供內部使用 |
| 繼承/實作關鍵字 | |
|
| 常見用途 | 提供共通實作與狀態 | 描述能力、角色 |
| JDK 範例 | |
|
4. 何時使用介面,何時使用抽象類別?
介面適用於:
- 你想描述類別「會做什麼」,而不關心它如何實作。
- 你需要讓類別能實作多個彼此獨立的能力。
- 例:Comparable(可比較)、Serializable(可序列化)、Runnable(可在執行緒中執行)。
抽象類別適用於:
- 你想定義所有子類別都會擁有的共通實作與狀態。
- 你需要所有子類別具備特定欄位,或具有已實作的方法。
- 繼承嚴格是「一對一」:類別只能繼承一個類別(一般或抽象)。
生活中的類比
- 介面就像「駕照」:如果你擁有它,你可以開車,但沒有人規定你要開哪一台車、用什麼方式開。
- 抽象類別就像「汽車的通用藍圖」:所有車都有方向盤、踏板、引擎,但各品牌會以自己的方式實作細節。
5. Java 標準函式庫中的範例
介面:Comparable
public interface Comparable<T> {
int compareTo(T o);
}
任何實作此介面的類別都必須實作方法 compareTo。例如,String、Integer、LocalDate 等等。
抽象類別:AbstractList
public abstract class AbstractList<E> implements List<E> {
// 預設實作部分 List 方法
// 其餘部分方法保持為抽象
}
AbstractList 已經實作了部分集合的行為(例如新增/刪除的方法),但仍保留一些方法為抽象,以便子類別能依自身需求實作。
6. 程式碼範例:實務比較
介面
建立一個介面以及實作它的類別。
public interface Printable {
void print();
}
public class Document implements Printable {
@Override
public void print() {
System.out.println("正在列印文件...");
}
}
抽象類別
接著是抽象類別及其子類別.
public abstract class Machine {
public void turnOn() {
System.out.println("機器已開機。");
}
public abstract void work();
}
public class Printer extends Machine {
@Override
public void work() {
System.out.println("印表機正在列印...");
}
}
同時使用兩者:類別同時繼承抽象類別並實作介面
public class SmartPrinter extends Machine implements Printable {
@Override
public void work() {
System.out.println("智慧型印表機正在運作...");
}
@Override
public void print() {
System.out.println("智慧型印表機正在列印...");
}
}
7. 多重介面實作:為什麼這麼強大
在 Java 中,類別只能繼承一個(abstract 或一般)父類別,但可以實作任意多個介面!這使得架構更具彈性且易於擴充。
public interface Scannable {
void scan();
}
public class MultiFunctionPrinter extends Machine implements Printable, Scannable {
@Override
public void work() {
System.out.println("多功能印表機正在運作...");
}
@Override
public void print() {
System.out.println("多功能印表機正在列印...");
}
@Override
public void scan() {
System.out.println("多功能印表機正在掃描...");
}
}
該如何選擇?
- 若你要設計具備共用狀態(例如欄位)的基礎功能,請使用抽象類別。
- 若你想為物件加上「能力標籤」(例如「會列印」、「可比較」、「可序列化」)——請使用介面。
- 如果不確定——先從介面開始。在 Java 中這被視為良好習慣:介面帶來更高的彈性與可擴充性。
8. 常見錯誤與陷阱
錯誤 №1:嘗試繼承多個類別——Java 不允許!
類別只能繼承一個類別,但可以實作多個介面。例如,class A extends B, C——錯誤;而 class A extends B implements X, Y, Z——可以。
錯誤 №2:把介面與類別的欄位搞混。
在介面中只能宣告常數(public static final)。不能宣告一般的狀態,例如,private int count;——編譯器會立刻阻止你。
錯誤 №3:沒有實作介面中的所有方法.
如果某個類別沒有實作介面中的任何一個方法——它必須宣告為 abstract,否則編譯器會報錯。
錯誤 №4:嘗試建立介面或抽象類別的實例.
這兩種型別都是「半成品」。它們只能被擴充,不能直接被建立:
Printable p = new Printable(); // 錯誤!
Machine m = new Machine(); // 錯誤!
錯誤 №5:以為介面可以有建構子。
介面不能有建構子,因為它們不描述物件的狀態。只有類別(一般與抽象)才有建構子。
GO TO FULL VERSION