1. ホーム
  2. java

javaのラムダこの参照

2023-09-19 11:01:50

質問

無名クラスをラムダ式に変換したい。しかし、この無名クラスは this というキーワードを使っています。

例えば、こんなシンプルなObserver/Observableパターンを書いてみました。

import java.util.ArrayList;
import java.util.Collection;

public static class Observable {
    private final Collection<Observer> notifiables = new ArrayList<>();

    public Observable() { }

    public void addObserver(Observer notifiable) { notifiables.add(notifiable); }
    public void removeObserver(Observer notifiable) { notifiables.add(notifiable); }

    public void change() {
        notifiables.forEach(notifiable -> notifiable.changed(this));
    }
}

public interface Observer {
    void changed(Observable notifier);
}

と、匿名クラス(thisキーワードを使用)を使ったサンプルコードです。

public class Main {

    public static void main(String[] args) {
        Observable observable = new Observable();
        observable.addObserver(new Observer() {
            @Override
            public void changed(Observable notifier) {
                notifier.removeObserver(this);
            }
        });
        observable.change();
    }
}

となっていますが、ラムダ式に変換すると

public class Main {

    public static void main(String[] args) {
        Observable observable = new Observable();
        observable.addObserver(notifier -> { notifier.removeObserver(this); });
        observable.change();
    }
}

こんなコンパイルエラーが出ます。

Cannot use this in a static context and in a non `static` context



public class Main {
    public void main(String[] args) {
        method();
    }

    private void method() {
        Observable observable = new Observable();
        observable.addObserver(notifier -> {
                notifier.removeObserver(this);
        });
        observable.change();
    }
}

コンパイルエラーは

The method removeObserver(Main.Observer) in the type Main.Observable is not applicable for the arguments (Main)

でラムダオブジェクトを参照する方法はありますか? this ?

どのように解決するのですか?

を参照することはできません。 this をラムダ式で参照することはできません。のセマンティックは this は、ラムダ式内から周囲のクラスのインスタンスのみを参照するように変更されました。ラムダ式の this をラムダ内部から参照する方法はありません。

問題なのは this の中で main() というメソッドがあります。main メソッドは静的であり、また this .

を使用する場合 this を使うと、内部クラスのインスタンスを参照することになります。 ラムダ式は内部クラスではありません。 this はラムダ式のインスタンスを参照しているわけではありません。ラムダ式を定義したクラスのインスタンスを参照しているのです。あなたの場合、それはMainのインスタンスになります。しかし、静的メソッドの中なので、インスタンスは存在しない。

これが、2つ目のコンパイルエラーの内容です。あなたはMainのインスタンスをメソッドに渡しました。しかし、あなたのメソッドの署名はObserverのインスタンスを必要とします。

更新してください。

この Java言語仕様15.27.2には :

無名クラス宣言に現れるコードとは異なり、ラムダ本体に現れる名前、thisやsuperキーワードの意味や、参照される宣言のアクセス性は周囲のコンテキストと同じです(ただし、ラムダパラメータは新しい名前を導入します)。

の透明性は この (明示的、暗黙的の両方)をラムダ式の本体で透明化すること、つまり周囲のコンテキストと同じように扱うことで、実装の柔軟性を高め、本体内の非限定名の意味がオーバーロードの解決に依存するのを防ぎます。

実際問題として、ラムダ式が自分自身について話す必要があるのは珍しいことです (自分自身を再帰的に呼び出すか、他のメソッドを呼び出すか)。 this, toString() ). ラムダ式が自分自身を参照する必要がある場合(例えば この のように)自分自身を参照する必要がある場合は、代わりにメソッド参照または無名の内部クラスを使用する必要があります。