Лямбда эта ссылка в java

Я хочу преобразовать anonymous class в lambda expression. Но этот анонимный класс я использует ключевое слово 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);
}

и этот пример кода с анонимным классом (используйте это ключевое слово):

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?

Ответ 1

Вы не можете ссылаться на this в выражении лямбда. Семантика this была изменена, чтобы ссылаться только на экземпляр окружающего класса, изнутри лямбда. Нет способа ссылаться на лямбда-выражение this изнутри лямбда.

Проблема заключается в том, что вы используете this в методе main(). Основной метод является статическим, и нет ссылки на объект, который представляет this.

Когда вы используете this внутри экземпляра внутреннего класса, вы ссылаетесь на экземпляр внутреннего класса. Выражение лямбда не является внутренним классом, this не ссылается на экземпляр лямбда-выражения. Он ссылается на экземпляр класса, в котором вы определяете выражение лямбда. В вашем случае это будет экземпляр Main. Но поскольку у вас есть статический метод, экземпляра нет.

Это ваша вторая ошибка компиляции. Вы передаете экземпляр Main для вашего метода. Но для вашей сигнатуры метода требуется экземпляр Observer.

Update:

Спецификация языка Java говорит:

Прозрачность этого (как явного, так и неявного) в теле лямбда-выражения, то есть, трактуя его так же, как и в окружающем контексте, обеспечивает большую гибкость для реализаций и предотвращает значение неквалифицированных имен в теле от зависимости от разрешения перегрузки.

Практически говоря, для выражения лямбды необычно говорить о себе (либо вызывать себя рекурсивно, либо ссылаться на другие его методы), в то время как чаще всего хочется использовать имена для обозначения вещей в прилагаемом класс, который иначе был бы затенен (это, toString()). Если необходимо, чтобы выражение лямбда ссылалось на себя (как бы через это), вместо этого следует использовать ссылку на метод или анонимный внутренний класс.

Ответ 2

Обходной путь 1

Ваш метод change() бросает ConcurrentModificationException в любом случае.

public class Main {
    public static void main(String[] args) {
        Observable observable = new Observable();
        final Observer[] a = new Observer[1];
        final Observer o = er -> er.removeObserver(a[0]); // !!
        a[0] = o;
        observable.addObserver(o);
        observable.change();
    }
}
public class Observable {
    private final java.util.Collection<Observer> n
        = java.util.new ArrayList<>();
    public void addObserver(Observer notifiable) {
        n.add(notifiable);
    }
    public void removeObserver(Observer notifiable) {
        n.add(notifiable);
    }
    public void change() {
        for (final Observer o : n.toArray(new Observer[n.size()])) {
            o.changed(this);
        }
    }
}
public interface Observer {
    void changed(Observable notifier);
}

Обходной путь 2

Я изменил changed(Observable) на changed(Observable, Observer), чтобы наблюдатель мог справиться сам.

public class Main {
    public static void main(String[] args) {
        Observable observable = new Observable();
        final Observer o = (er, ee) -> er.removeObserver(ee); // !!
        observable.addObserver(o);
        observable.change();
    }
}
public class Observable {
    private final java.util.Collection<Observer> n
        = new java.util.ArrayList<>();
    public void addObserver(Observer notifiable) {
        n.add(notifiable);
    }
    public void removeObserver(Observer notifiable) {
        n.add(notifiable);
    }
    public void change() {
        for (final Observer o : n.toArray(new Observer[n.size()])) {
            o.changed(this, o);
        }
    }
}
public interface Observer {
    void changed(Observable notifier, Observer notifiee);
}