匿名內部類是 Java 語法糖中極具實用價值的一項特性,它允許在聲明類的同時創建對象,極大簡化了「一次性實現接口/繼承類」的代碼量。本文通過 5 個遞進式示例,系統講解語法規則、適用場景與最佳實踐。
abstract class Person {
public abstract void eat();
}
class Child extends Person {
@Override public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
痛點:Child 類若僅用一次,單獨定義顯得冗餘。
public class Demo {
public static void main(String[] args) {
Person p = new Person() { // 1. new 父類類型()
@Override // 2. 大括號內實現抽象方法
public void eat() {
System.out.println("eat something");
}
}; // 3. 分號結束語句
p.eat();
}
}
關鍵語法:`new 父類型() { 方法實現 }`,實際創建的是父類的匿名子類實例。
interface Person {
void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
@Override
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
接口同理,`new 接口類型() { 實現方法 }` 創建實現類實例。
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start(); // 輸出:1 2 3 4 5
}
}
繼承 Thread 並重寫 run(),適合簡單線程任務。
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
new Thread(r).start(); // 輸出:1 2 3 4 5
}
}
優勢:實現 Runnable 而非繼承 Thread,符合「組合優於繼承」原則,便於線程池復用任務對象。
當接口為函數式接口(單一抽象方法)時,匿名內部類可簡化為 Lambda:
// 示例 3 接口版
Person p = () -> System.out.println("eat something");
// 示例 5 Runnable 版
Runnable r = () -> {
for (int i = 1; i <= 5; i++) System.out.print(i + " ");
};
new Thread(r).start();
注意:Lambda 不創建額外類文件,性能更優;但需訪問外部 final/Effectively final 變量,且無法使用 this 當前實例引用。
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 一次性事件監聽/回調 | Lambda (Java 8+) | 代碼極簡、無額外 class 文件 |
| 需訪問外部非 final 變量/使用 this | 匿名內部類 | 捕獲語義更靈活 |
| 實現多個方法的接口/抽象類 | 匿名內部類 | Lambda 不支援 |
| 複雜邏輯/需復用 | 獨立類/Record/靜態內部類 | 可讀性、可測試性更好 |
匿名內部類是 Java 早期函數式編程的妥協方案,Java 8 Lambda 大幅替代了其高頻場景。理解其機制有助於閱讀舊代碼庫,並在 Lambda 不適用時做正確選擇。