Java 8開始有一個新的名詞叫effectively final的意思如下。,
所謂的effectively final變數是指雖然前面沒有final
修飾,但宣告初值後就不再被改變的變數,官方說明如下。
A variable or parameter whose value is never changed after it is initialized is effectively final
如果本地類別(local class)及內部類別(inner class)要存取外部區塊(enclosing block)的變數,則該變數必須為final或effectively final變數才可以正確編譯。
例如下面demo()
方法宣告變數n
及初值後又被n++
修改,因此n
不是effectively final變數,在內部類別中存取n
時會編譯錯誤。
void demo() {
int n = 0; // 宣告變數n
Thread t = new Thread(new Runnable() { // 匿名內部類別
@Override
public void run() {
n++; // n的內容被修改,所以n不是effectively final
System.out.println(n); // n編譯錯誤
}
});
t.start();
}
若將n++
移除,則n
宣告後就不被修改屬於effectively final,所以內部類別可以存取。
void demo() {
int n = 0; // 宣告變數n
Thread t = new Thread(new Runnable() { // 匿名內部類別
@Override
public void run() {
System.out.println(n); // n編譯正確
}
});
t.start();
}
這點在使用lambda forEach()
時要注意。過去用for each遍歷陣列或集合物件時常會引用外層區塊的物件並修改,例如
int n = 0;
List<Employee> employeeList = getEmployeeList(int age);
for (Employee e : employeeList) {
n++; // 編譯正確
// ...
}
但由於forEach()
的傳入參數其實是一個實作Consumer
的內部匿名類別,所以只能引用外部區塊的final及effectively final的變數。
int n = 0; // 宣告變數n及初值
List<Employee> employeeList = getEmployeeList(int age);
employeeList.forEach(e -> {
n++; // 編譯錯誤
// ...
});
沒有留言:
張貼留言