Java 無多重繼承,但通過接口實現多重實現;繼承表現為多態。本文通過「父親打兒子」生動案例,講透繼承實現多態、接口實現功能擴展、兩者協作的設計原則(開閉原則、里氏替換),並對比修改舊功能與增加新功能的正確姿勢。
public class Father {
public void hitChild() {
System.out.println("臭崽子,你站著,不打你我不是你爹!");
}
}
public class Son1 extends Father {
@Override public void hitChild() {
System.out.println("為什麼打我?我做錯什麼了!");
}
}
public class Son2 extends Father {
@Override public void hitChild() {
System.out.println("我知道錯了,別打了!");
}
}
public class Son3 extends Father {
@Override public void hitChild() {
System.out.println("我跑,你打不著!");
}
}
Father father = new Father(); father.hitChild(); // 父類實現
father = new Son1(); father.hitChild(); // 子類 1 實現
father = new Son2(); father.hitChild(); // 子類 2 實現
father = new Son3(); father.hitChild(); // 子類 3 實現
臭崽子,你站著,不打你我不是你爹! 為什麼打我?我做錯什麼了! 我知道錯了,別打了! 我跑,你打不著!
結論:父類引用指向不同子類對象,調用同一方法產生不同行為 —— 這就是多態。
public class Son implements Father1, Father2, Father3 { /* 相當於 C++ 多重繼承 */ }
一個類可實現多個接口,獲得多組契約能力。
| 維度 | 繼承 | 接口 |
|---|---|---|
| 核心目的 | 修改舊功能(Override 重寫實現) | 增加新功能(Implements 新契約) |
| 對舊代碼影響 | 子類可改變父類行為,風險較大 | 不修改舊類,零風險擴展 |
| 開閉原則 (OCP) | 違反:修改父類/子類即修改 | 符合:對擴展開放,對修改關閉 |
| 里氏替換 (LSP) | 子類必須能替換父類不破壞語義 | 實現類必須滿足接口契約 |
// 二代子類:改變「打孩子」的具體實現
public class Son11 extends Son1 {
@Override public void hitChild() {
System.out.println("我是二代子類對此方法的新實現");
}
}
// 1. 定義新能力接口
public interface PetInterface {
void pet(); // 寵孩子
}
// 2. 二代子類:繼承舊能力 + 實現新接口
public class Son11 extends Son1 implements PetInterface {
@Override public void pet() {
System.out.println("父親很愛我!");
}
}
當「是一種」關係不穩固時,優先用組合/委派替代繼承:
// 繼承版(耦合緊)
class Bird extends Animal { void fly() {} }
class Penguin extends Bird { /* 企鵝不會飛,違反 LSP */ }
// 組合版(靈活)
interface Flyable { void fly(); }
class Bird { Flyable flyBehavior; }
class Eagle extends Bird { Bird() { flyBehavior = new SoarFly(); } }
class Penguin extends Bird { Bird() { flyBehavior = new NoFly(); } }
extends)。implements)。掌握「繼承做修改,接口做擴展」這一核心直覺,配合設計原則,能讓你的類體系既靈活擴展又穩健演進,避免「繼承地獄」與「接口爆炸」兩極陷阱。