this 與 final
June 24, 2022lambda 運算式並不是匿名函式的語法糖,來看一下接下來的程式,先想想看結果會如何顯示?
package cc.openhome;
import static java.lang.System.out;
class Hello {
Runnable r1 = new Runnable() {
public void run() {
out.println(this);
}
};
Runnable r2 = new Runnable() {
public void run() {
out.println(toString());
}
};
public String toString() {
return "Hello, world!";
}
}
public class Main {
public static void main(String[] args) {
new Hello().r1.run();
new Hello().r2.run();
}
}
結果會顯示像是 cc.openhome.Hello$1@6d06d69c 與 cc.openhome.Hello$2@7852e922 之類的訊息,這是因為 this
以及 toString()
(也就是 this.toString()
)實際對象,實際上會來自匿名類別對應的實例,也就是 Runnable
實例,你沒有定義 Runnable
的 toString
,顯示結果是 Object
預設的 toString
傳回字串。
Lambda運算式 與 this
再來看看接下來的程式,它會顯示什麼?
package cc.openhome;
import static java.lang.System.out;
class Hello {
Runnable r1 = () -> out.println(this);
Runnable r2 = () -> out.println(toString());
public String toString() {
return "Hello, world!";
}
}
public class Main {
public static void main(String[] args) {
new Hello().r1.run();
new Hello().r2.run();
}
}
如果 Lambda 運算式只是匿名類別的語法蜜糖,那麼結果也該是顯示 cc.openhome.Hello$1@6d06d69c 與 cc.openhome.Hello$2@7852e922 之類的訊息,事實上,執行結果會是顯示兩次的 "Hello, world!"
。
也就是說,Lambda 運算式本體中的 this
與 toString()
(也就是 this.toString()
)實際對象,是來自 Lambda 的上下文環境(Context),也就是建構出來的 Hello
實例,因為是 Hello
類別包圍了 Lambda 運算式,範例中定義了 Hello
類別的 toString
傳回 "Hello, world!"
字串,執行時才會顯示兩次的 "Hello, world!"
。
final 與 Lambda 運算式
如果要在匿名內部類別中存取區域變數,該區域變數必須等效於 final
,否則會發生編譯錯誤,例如以下不會編譯錯誤:
String[] names = {"Justin", "Monica", "Irene"}; // 加上 final 也可以
Runnable runnable = new Runnable() {
public void run() {
for(String name : names) {
System.out.println(name);
}
}
};
因為 Runnable
符合函式介面定義,可以改為 Lambda 運算式:
String[] names = {"Justin", "Monica", "Irene"};
Runnable runnable = () -> {
for(String name : names) {
System.out.println(name);
}
};
如果 Lambda 運算式中捕獲的區域變數本身等效於 final
區域變數,可以不用在區域變數上加上 final
。不過,可以在 Lambda 運算式中改變被捕獲的區域變數值嗎?
不行!Java 特意禁止你捕獲可變動的區域變數(在 JavaScript 等語言可以做到),因為 Java 會想要採用 Lambda 的理由之一,是想進一步支援平行程式設計,Lambda 運算式中可變動的區域變數,通常也代表著運算式本體中會改變被捕獲變數參考的物件狀態,改變物件狀態在平行程式中代表著可能必須處理同步鎖定問題,以禁止捕獲可變動的區域變數來避免了這類的問題。