Java 7は、次のコードで「囲んでいるスコープで定義された非最終ローカル変数メッセージを参照できません」と言っていました。
public class Runner {
public static void main(String[] args) {
String message = "Hello world";
new Runnable() {
@Override
public void run() {
System.out.println(message);
}
}.run();
}
}
Java8はそうではありません。
これは、Javaに関数型プログラミング機能を追加することに関するものだと思われます。
コードを同様に処理しますか?
Java 8は、変更されないため、暗黙的にmessage
をfinalにします。コード内の任意の場所で変更してみると、コンパイルエラーが発生します(これにより、暗黙のfinal
が削除されるため)。
これは事実上最終と呼ばれます。引用 ドキュメントから :
ただし、Java SE 8以降、ローカルクラスは、最終または実質的に最終である、囲んでいるブロックのローカル変数およびパラメーターにアクセスできます。初期化後に値が変更されない変数またはパラメーター事実上最終的です。
変数message
は 事実上final です。言語参照からの引用
変数が事実上finalである場合、その宣言にfinal修飾子を追加しても、コンパイル時エラーは発生しません。
したがって、message
参照は内部クラス内のどこにも変更されないため、コンパイラはそれを事実上最終的なものとして扱います。
これはエラーをスローします:
new Runnable() {
@Override
public void run() {
message = "hey";
System.out.println(message);
}
}.run();
Java7コンパイラがエラーをスローする理由は、 lambdas の仕様変更が原因です。
使用されているが使用されていないローカル変数、仮パラメーター、または例外パラメーターラムダ式で使用されているが宣言されていないローカル変数、仮パラメーター、または例外パラメーターは、finalとして宣言するか、実質的にfinal(§4.12.4)にするか、コンパイルする必要があります。 -使用を試みるとタイムエラーが発生します。
匿名の内部クラスとラムダは同じルールを共有します。
はい、そうです。
基本的に彼らは、ローカル変数が「事実上最終的」であるとき、つまりその値が決して変更されないとき、コンパイラーはコードを分析することによってすでに決定しなければならないことに気づきました。
したがって、セマンティクスは変更されていません。変数final
を明示的に宣言する必要はありませんが、再割り当てされないようにする必要があります。