前幾章建立了類別繼承與封裝基礎。當系統增長,我們需要定義行為契約而非實作細節。介面與抽象類別是實現「面向介面程式設計」的兩大核心機制:前者定義**純粹契約**,後者提供**部份實作與共享狀態**。本章深入剖析兩者的語法、繼承規則、多重實作與設計抉擇。
介面可繼承另一介面,形成契約層級:
interface Cup {
void addWater(int ml);
void drinkWater(int ml);
}
// 延伸契約:加入刻度查詢
interface MetricCup extends Cup {
int getWaterVolume(); // 新增方法簽名
}
類別僅支援單繼承,但**介面可同時繼承多個父介面**,實現契約組合:
interface Player {
void play();
void pause();
}
interface Displayable {
void display();
}
// 組合三大契約:容器、播放器、顯示器
interface MusicCup extends Cup, Player, Displayable {
void setVolume(int level); // 專屬擴展
}
實作 MusicCup 的類別必須提供 **Cup + Player + Displayable + 自身** 的所有方法實作。這是 Java 介面相對類別繼承的巨大優勢——**契約可自由組合,不受單繼承限制**。
abstract class:宣告為抽象,**不可直接實例化**。abstract method:無主體、無大括號、以分號結尾,**強制子類別實作**(除非子類別也為抽象)。abstract。abstract class Food {
// 共享欄位
protected String name;
// 建構子:供子類別鏈式呼叫
public Food(String name) { this.name = name; }
// 抽象方法:定義契約,強制實作
public abstract void eat();
// 具體方法:提供預設行為,子類別可選擇性覆寫
public void happyFood() {
System.out.println(name + ":Good! Eat Me!");
}
// 普通方法:內部邏輯
public final String getName() { return name; } // final 防覆寫
}
class Corn extends Food {
public Corn() { super("玉米"); }
@Override
public void eat() {
System.out.println("剝皮 → 煮熟 → 咬著吃");
}
}
class Wheat extends Food {
public Wheat() { super("小麥"); }
@Override
public void eat() {
System.out.println("磨粉 → 和麵 → 蒸烤 → 品嚐");
}
}
// 使用多型
public class Test {
public static void main(String[] args) {
Food[] foods = { new Corn(), new Wheat() };
for (Food f : foods) {
f.eat(); // 多型調用各自實作
f.happyFood(); // 繼承預設行為
}
}
}
| 維度 | 介面 (interface) | 抽象類別 (abstract class) |
|---|---|---|
| 核心目的 | 定義**行為契約**(能做什麼) | 提供**骨架實作**(怎麼做的基礎) |
| 方法實作 | Java 8 前:全抽象 Java 8+:可有 default/static 方法 | 可混合抽象與具體方法 |
| 欄位 | 僅 static final 常數 | 可有實例欄位、靜態欄位、任意修飾符 |
| 建構子 | ❌ 無 | ✅ 有(供子類呼叫 super()) |
| 繼承關係 | 多重實作 (implements A, B)多重繼承 ( extends A, B) | 單一繼承 (extends Base) |
| 存取修飾符 | 方法隱含 public(Java 9+ 可 private) | 任意修飾符 |
| 適用場景 | 跨領域契約(Serializable、Comparable、Runnable)、API 邊界、SPI 擴展點 | 同領域類別共享狀態/邏輯(Template Method 模式)、框架基類 |
interface Cup {
void addWater(int ml);
void drinkWater(int ml);
// 預設實作:子介面/實作類可選擇性覆寫
default void rinse() {
System.out.println("清水沖洗...");
}
// 靜態工具方法
static Cup createDefault() {
return new SimpleCup();
}
}
現在介面也能提供「預設實作」,減少對抽象類別的依賴。
// 1. 核心契約
interface Repository {
T findById(Long id);
List findAll();
void save(T entity);
void delete(T entity);
}
// 2. 共享骨架:連線管理、事務模板、通用查詢邏輯
abstract class AbstractJdbcRepository implements Repository {
protected DataSource dataSource;
public AbstractJdbcRepository(DataSource ds) { this.dataSource = ds; }
// 模板方法:具體流程
public final void save(T entity) {
try (Connection c = dataSource.getConnection()) {
c.setAutoCommit(false);
doSave(c, entity); // 抽象:子類實作具體 SQL
c.commit();
} catch (SQLException e) { throw new RepositoryException(e); }
}
protected abstract void doSave(Connection c, T entity) throws SQLException;
// 其他模板方法...
}
// 3. 具體實作
class UserRepository extends AbstractJdbcRepository {
public UserRepository(DataSource ds) { super(ds); }
@Override protected void doSave(Connection c, User u) throws SQLException {
// 實際 INSERT/UPDATE SQL
}
// 實作其他抽象方法...
}
default/static 方法讓介面更強大,但**狀態共享仍需抽象類**。extends AbstractBase implements InterfaceA, InterfaceB 享受兩全其美。設計原則:「程式對介面,不對實作」。介面是系統的**邊界與契約**;抽象類是**內部復用的工具**。良好的架構,介面在外圍,抽象類在核心。