IT박스

Java의 Lambda이 참조

itboxs 2020. 11. 28. 08:51
반응형

Java의 Lambda이 참조


나는를 변환 할 anonymous classA를 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);
}

익명 클래스가있는이 샘플 코드 (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()입니다. 기본 메서드는 정적이며을 나타내는 개체에 대한 참조가 없습니다 this.

this내부 클래스의 인스턴스 내부에서 사용할 때 내부 클래스의 인스턴스를 참조하는 것입니다. 람다 식은 내부 클래스 this가 아니며 람다 식의 인스턴스를 참조하지 않습니다. 람다 식을 정의하는 클래스의 인스턴스를 참조합니다. 귀하의 경우에는 Main의 인스턴스가 될 것입니다. 그러나 정적 메서드에 있기 때문에 인스턴스가 없습니다.

이것은 두 번째 컴파일 오류가 알려주는 것입니다. Main 인스턴스를 메서드에 넘깁니다. 그러나 메서드 서명에는 Observer 인스턴스가 필요합니다.

최신 정보:

Java 언어 사양 15.27.2는 말합니다 :

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).

The transparency of this (both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.

Practically speaking, it is unusual for a lambda expression to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (this, toString()). If it is necessary for a lambda expression to refer to itself (as if via this), a method reference or an anonymous inner class should be used instead.


Workaround 1

Your change() method throws ConcurrentModificationException anyway.

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);
}

Workaround 2

I changed the changed(Observable) to changed(Observable, Observer) so that an observer can handle itself.

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);
}

참고URL : https://stackoverflow.com/questions/24202236/lambda-this-reference-in-java

반응형