在Java中的lambda引用
我想将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(); } }
但是当我将其转换为lambdaexpression式时:
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)
所以我的问题是: 有没有办法引用这个“lambda对象”?
你不能在lambdaexpression式中引用this
。 这个语义已经被改变,只能从lambda内引用周围类的实例。 没有办法从lambda内部引用lambdaexpression式。
问题是你在main()
方法中使用this
。 主要的方法是静态的,没有引用表示this
的对象。
当你在一个内部类的实例中使用this
时,你引用了内部类的实例。 lambdaexpression式不是内部类, this
不是引用lambdaexpression式的实例。 它是引用到你定义lambdaexpression式的类的实例。在你的情况下,它将是一个Main的实例。 但是由于你是静态的,所以没有实例。
这是你的第二个编译错误告诉你的。 您将Main的一个实例移交给您的方法。 但是你的方法签名需要一个Observer实例。
更新:
Java语言规范说 :
在lambdaexpression式的主体中的透明性(即明确的和隐含的) – 也就是说,将其与周围的上下文一样处理 – 为实现提供了更大的灵活性,并且防止身体中非限定名称的含义依赖于在重载parsing上。
从实际的angular度讲,lambdaexpression式需要谈论自己(或者调用自己的recursion或者调用其他方法)是不常见的,而想要使用名称引用封闭类中的东西更常见否则被遮蔽( this,toString() )。 如果需要lambdaexpression式引用自身(就像通过这个 ),应该使用方法引用或匿名内部类。
解决方法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); }