web-dev-qa-db-ja.com

包含スコープで定義されたローカル変数ログは、最終的または実質的に最終的でなければなりません

ラムダとJava8は初めてです。次のエラーに直面しています。

包含スコープで定義されたローカル変数ログは、最終的または実質的に最終的でなければなりません

public JavaRDD<String> modify(JavaRDD<String> filteredRdd) {

    filteredRdd.map(log -> {

        placeHolder.forEach(text -> {

            //error comes here
            log = log.replace(text, ",");

        });

        return log;

    });

    return null;
}
18
Balaji Reddy

メッセージは問題が何であるかを正確に示しています:変数logはfinal(つまり、キーワードfinalを運ぶ)であるか、事実上final(つまり:ラムダの外に値を割り当てるだけですonce)。それ以外の場合は、ラムダステートメント内でその変数を使用できません。

しかし、もちろん、これはlogの使用と競合します。重要なのは、ラムダ内から外部に何かを書き込むことはできないということです...そのため、前に戻って、何をするつもりかについて他の方法を探す必要があります。

その意味では、コンパイラを信じてください。

それ以外に、1つcoreを理解する必要があります。not書き込み可能なローカル変数を使用できます。ローカル変数は、実行時にラムダのコンテキストに「コピー」されます。確定的な動作を実現するために、ローカル変数は読み取りのみ可能で、定数でなければなりません。

ユースケースが何らかのオブジェクトにwriteする場合、それは、たとえば、包含クラスのフィールドである必要があります!

要するに、短い話:

  • localラムダ内で使用される(読み取り)変数は定数のように機能する必要があります
  • ローカル変数にwriteすることはできません!
  • またはその逆:書き込みが必要な場合は、たとえば周囲のクラスのフィールドを使用する必要があります(またはコールバックメソッドを提供する必要があります)

この制限の理由は、Java言語機能ローカル変数(匿名)内からアクセスしたことの理由)と同じです。内部クラスは(事実上)最終でなければなりません。

この回答 rgettmanが詳細について説明します。 rgettmanは制限を明確に説明しており、ラムダ式の動作は匿名の内部クラスの動作と同じであるべきなので、私はその答えにリンクしています。ただし、このような制限はクラスまたはインスタンス変数には存在しないことに注意してください。これの主な理由は少し複雑であり、Roedy Greenが何をするよりもよく説明することはできません here 。ここにのみコピーして、1か所にコピーします。

ルールは匿名であり、内部クラスは囲んでいるメソッドの最終的なローカル変数にのみアクセスできます。どうして?内部クラスのメソッドは後で呼び出される可能性があるため、それを生成したメソッドが終了した後、たとえばAWT(Advanced Windowing Toolkit)イベントによる。ローカル変数はもうなくなっています。その後、匿名クラスは、匿名の内部クラスオブジェクト内のコンパイラによって密かに必要とされるものだけのフラッシュフリーズコピーを処理する必要があります。なぜローカル変数はfinalでなければならないのですか?コンパイラは、最終パラメータ以外のローカル変数のコピーも取得できませんでしたか?その場合、変数のコピーが2つあります。呼び出し元と呼び出し先のパラメーターのコピーのように、それぞれが独立して変更できますが、どちらのコピーにも同じ構文を使用してアクセスします。これは混乱を招くでしょう。したがって、Sunはローカルが最終的であると主張しました。これにより、実際に2つのコピーが存在することは無関係になります。

匿名クラスが呼び出し元の最終的なローカル変数にアクセスする機能は、一部のローカル変数を追加のコンストラクターパラメーターとして自動的に渡すための、単なる構文上の砂糖です。全体が希釈されたオードクラッジのにおいがします。

12
Kedar Mhaswade

メソッドの内部クラスは、周囲のメソッドの値を変更できないことに注意してください。 forecachの2番目のラムダ式は、周囲のメソッド変数(ログ)にアクセスしようとしています。

これを解決するには、それぞれにラムダを使用することを避け、それぞれにシンプルを使用して、ログ内のすべての値を再配置します。

        filteredRdd.map(log -> {
        for (String text:placeHolder){
            log = log.replace(text,",");
        }
        return log;
    });
3
Mr.Q