2016-11-29 19 views
4

私は、2つのコードサンプルを持っている:のJava 8のラムダ変数のスコープ

int[] idx = { 0 }; 
List<String> list = new ArrayList<String>(); 
list.add("abc"); 
list.add("def"); 
list.add("ghi"); 
list.stream().forEach(item -> { 
    System.out.println(idx[0] + ": " + item); 
    idx[0]++; 
}); 

は正常に動作し。

このコードはコンパイルエラー有するが:

int idx = 0; 
List<String> list = new ArrayList<String>(); 
list.add("abc"); 
list.add("def"); 
list.add("ghi"); 
list.stream().forEach(item -> { 
    System.out.println(idx + ": " + item); 
    idx++; 
}); 

と言っ:

Local variable idx defined in an enclosing scope must be final or effectively final. 

唯一の違いはidx intまたは整数配列です。

根本原因は何ですか?

+1

なぜ私はあなたがidxを静的にして、それが正常に動作するメインメソッドの外に移動する場合、私は傾けることはできません – XtremeBaumer

+0

@XtremeBaumerはい、私はidxを静的にして、 、うまく動作します。しかし、なぜ?詳細を共有するのに役立つでしょうか? – coderz

+0

リストには '.forEach'が適用され、ストリームには適用されないことを付け加えておきます: ' list.forEach(item - > {{'' – Tchopane

答えて

6

根本的な原因は、JVMがidxintまたはいくつかの不変のタイプ(例えばString)である場合idx++を実行するために必要とされるものであり、ローカル変数への参照を構築するメカニズムを欠いていることです。 idxを突然変異させようとするので、その値をキャプチャするだけでは十分ではありません。 Javaは参照を取得し、それを介して値を変更する必要があります。

配列は参照オブジェクトなので、配列を使用するとJavaにこの問題はありません。 Javaは決して変更されない配列参照を取得し、その変化しない参照を使用してオブジェクトを変更することができます。 Java配列は変更可能であるため、配列自身が必要なレベルの間接参照を提供します。

I tried make idx static and move it outside the main method, working fine. But why?

この状況では、ラムダがプリミティブ型のローカル変数への参照を取得する必要がないためです。静的変数への参照はすぐに利用できるので、それをキャプチャすることに問題はありません。

同様に、idxをメンバ変数にしてインスタンスメソッド内でラムダを使用すると、コードが機能します。これにより、ラムダはidxフィールドをthisオブジェクトに変更することができ、自由にキャプチャできます。

+1

Javaには一般的にローカル変数へのポインタがありません。変数の型がプリミティブかどうかは関係ありませんが、ローカル変数は変更できません。そしてそれは良いことです。ローカル変数は固定された存続時間を持ち、長く続く可能性のあるポインタを持つことは、まさにJavaが防止するものです。 – Holger

2

あなたの意見を部分的に説明しています。 Java 8コードの初期化された配列は、と実質的には最終的にとみなされます。その値は初期化後も変更されないためです。これが、コードのint[] idx = { 0 };バージョンが通過している理由です。だから、もしあなたがint idxを効果的に最終的にしたら、それはまた成功するだろうと私は期待しています。これを行う1つの方法は、に正式にというように宣言することで、この変数をfinalにすることです。つまり、final int idx = 0です。

+1

「初期化後に値を変更できない」と思われる理由は何ですか?直後に 'idx = new int [42];'と書くだけです。 'idx'はコードがそれをしないので、事実上最終的です。 – Holger

1

初期化後に値が決して変更されない変数またはパラメータは、実質的に最終的にです。ケース1では

int[] idxあなたはidx = {1};idx[0]++;を交換する場合は、変更しないでくださいはケース2ではエラー

をコンパイルします:あなたはidx++を削除する場合

。それはうまくコンパイルされます

関連する問題