匿名內部類 = **無名稱** + **繼承單一類別** 或 **實作單一介面** + **現場建立實例**。
// 繼承類別
new SuperClass(constructorArgs) {
// 類別主體:覆寫方法、新增欄位/方法
@Override public void method() { ... }
};
// 實作介面
new Interface() {
@Override public void method() { ... }
};
final,不可被繼承。{ ... }。
Runnable r = new Runnable() {
private final int id;
private final String name;
// 實例初始化區塊:執行順序在父類建構子之後、匿名類建構子邏輯之前
{
id = generateId();
name = "Worker-" + id;
System.out.println("Initializing " + name);
}
@Override public void run() {
System.out.println(name + " running...");
}
};
// 原始碼
void process(String param) {
Runnable r = new Runnable() {
public void run() {
System.out.println(param); // 捕獲 param
}
};
}
// 編譯器生成的類別 (概念性)
class Process$1 implements Runnable {
private final String param; // 複本
Process$1(String param) { // 合成建構子
this.param = param;
}
public void run() { System.out.println(this.param); }
}
// 實際呼叫
new Process$1(param);
關鍵點:匿名類別**不直接存取外部變數**,而是在建構時**複製值/引用**到自身欄位。若外部變數後續修改,內部欄位**不會同步更新**,導致語義不一致。
Java 8+ **effectively final**:變數雖未宣告 final,但編譯後從未被重新賦值,編譯器視同 final 允許捕獲。
// 錯誤:無法在初始化區塊中引用 this (尚未完全建構)
// new Runnable() { { this.run(); } }
// 正確:用欄位保存自我引用 (罕見需求)
Runnable r = new Runnable() {
Runnable self = this; // 編譯錯誤:this 在初始化區塊不可用
// 解決:分兩步,先建立再設定 (失去匿名優勢)
};
匿名類別**難以自我引用**,需遞迴時改用具名類別或 Lambda。
Outer$1),跨 JVM 版本/編譯器不穩定。
class Outer {
Inner inner;
void leak() {
inner = new Inner() { // 匿名內部類持有外部類引用
void doSomething() {}
};
}
}
Outer.this)。static 內部類、Lambda (無外部引用)、或弱引用。| 特性 | 匿名內部類 | Lambda |
|---|---|---|
| 語法 | 冗長 | 簡潔 () -> {} |
| 函數式介面 | 支援任意介面/類別 | **僅支援單一抽象方法 (SAM) 介面** |
| this 引用 | 指向匿名類別實例 | 指向**外部類實例** |
| 捕獲變數 | final/effectively final | 相同規則 |
| 序列化 | 不穩定 | 標準化支援 |
| 效能 | 建立類別載入開銷 | invokedynamic + LambdaMetafactory,**極快** |
建議:能用 Lambda 就用 Lambda;需實作多方法介面、繼承類別、複雜邏輯時用匿名內部類或具名類別。
JButton btn = new JButton("Click");
btn.addActionListener(new ActionListener() {
private int clickCount = 0; // 狀態封裝在匿名類別中
@Override
public void actionPerformed(ActionEvent e) {
clickCount++;
System.out.println("Clicked " + clickCount + " times");
if (clickCount >= 3) {
btn.setEnabled(false); // 捕獲外部 final btn
}
}
});