來看一下接下來的程式,先想想看結果會如何顯示?
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()
傳回字串。再來看看接下來的程式,它會顯示什麼?
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!"。
在 匿名內部類別 中談過,JDK7之前,如果要在匿名內部類別中存取區域變數,則該區域變數必須是final
,否則會發生編譯錯誤,而在在JDK8中,如果變數本身等效於final
區域變數,也就是說,如果變數不會在匿名類別中有重新指定的動作,就可以不用加上final
關鍵字。例如以下在JDK8中不會有錯:
String[] names = {"Justin", "Monica", "Irene"}; // JDK8 前必須加上 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表示式中改變被捕獲的區域變數值嗎?
不行!JDK8特意禁止你捕獲可變動的區域變數(在JavaScript或Scala等語言中可以做到),因為JDK8會想要採用Lambda的理由之一,是想進一步支援並行程式設計,Lambda表示式中可變動的區域變數,通常也代表著運算式本體中會改變被捕獲變數參考的物件狀態,改變物件狀態在並行程式中代表著可能必須處理同步鎖定問題,JDK8以禁止捕獲可變動的區域變數來避免了這類的問題。