Java 匿名類別 (Anonymous Class)

匿名類別是沒有名稱的內部類別,用於建立一次性使用的類別實例,常用於實作介面或繼承類別。

基本語法

// 實作介面的匿名類別
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("執行中");
    }
};

// 繼承類別的匿名類別
Thread thread = new Thread() {
    @Override
    public void run() {
        System.out.println("執行緒執行中");
    }
};

實作介面

interface Greeting {
    void sayHello(String name);
}

// 使用匿名類別
Greeting greeting = new Greeting() {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello, " + name + "!");
    }
};

greeting.sayHello("Alice");  // Hello, Alice!

繼承抽象類別

abstract class Animal {
    abstract void makeSound();
    
    void sleep() {
        System.out.println("睡覺中...");
    }
}

Animal dog = new Animal() {
    @Override
    void makeSound() {
        System.out.println("汪汪!");
    }
};

dog.makeSound();  // 汪汪!
dog.sleep();      // 睡覺中...

存取外部變數

public void process() {
    final String message = "Hello";  // 必須是 final 或 effectively final
    int count = 10;  // effectively final
    
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println(message);
            System.out.println(count);
            // count++;  // 錯誤!不能修改
        }
    };
}

帶建構子參數

abstract class Person {
    String name;
    
    Person(String name) {
        this.name = name;
    }
    
    abstract void introduce();
}

Person person = new Person("Alice") {
    @Override
    void introduce() {
        System.out.println("我是 " + name);
    }
};

常見用途

事件處理

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按鈕被點擊");
    }
});

Comparator

List<String> list = Arrays.asList("banana", "apple", "cherry");

Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
});

執行緒

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("執行緒執行中");
    }
}).start();

匿名類別 vs Lambda

Java 8 後,單一抽象方法的介面可以用 Lambda 取代:

// 匿名類別
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Lambda(更簡潔)
Runnable r2 = () -> System.out.println("Hello");

// Comparator
Comparator<String> c1 = new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
};

Comparator<String> c2 = (s1, s2) -> s1.compareTo(s2);
Comparator<String> c3 = String::compareTo;

何時使用匿名類別

使用匿名類別:

  • 需要多個方法
  • 需要存取 this(指向匿名類別本身)
  • 需要繼承類別

使用 Lambda:

  • 單一抽象方法的介面
  • 簡短的實作
// 匿名類別的 this
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println(this.getClass());  // 匿名類別
    }
};

// Lambda 的 this
Runnable r2 = () -> {
    System.out.println(this.getClass());  // 外圍類別
};

重點整理

  • 匿名類別沒有名稱,建立時同時定義和實例化
  • 可以實作介面或繼承類別
  • 只能存取 final 或 effectively final 的外部變數
  • 單一抽象方法介面建議用 Lambda 取代
  • 匿名類別中的 this 指向匿名類別本身