当它们作为lambda或方法引用传递时,如何实现可移除的侦听器?

我想知道在没有太多接口的情况下在 Java 中实现某种 observable 的好方法是什么。

我认为使用预定义的功能接口会很好。在这个例子中,我使用 aString Consumer来表示一个接受一个字符串作为通知的监听器。

class Subject {

  List<Consumer<String>> listeners = new ArrayList<>();
  
  void addListener(Consumer<String> listener) { listeners.add(listener); }

  void removeListener(Consumer<String> listener { listeners.remove(listener); }

  ...
}

class PrintListener {
  public void print(String s) { System.out.println(s); }
}

Subject subject = new ...
PrintListener printListener = new ...
subject.add(printListener); // Works, I find it in the listener list
subject.remove(printListener); // Does NOT work. I still find it in the list

我找到了解释:

Consumer<String> a = printListener::print;
Consumer<String> b = printListener::print;

// it holds:
// a==b       : false
// a==a       : true
// a.equals(b): false
// a.equals(a): true

所以我不能按原样使用 lambdas/函数指针。

总有一种选择可以恢复旧的接口,我们注册对象实例而不是 lambdas。但我希望有更轻量的东西。

编辑:

从目前的回应中,我看到以下方法:

a) 返回Handle保存原始引用的
a b) 自己存储原始引用
c) 返回一些可用于subject.remove()代替原始引用的ID(整数)

我倾向于喜欢 a)。您仍然必须跟踪Handle.

回答

I'm using rjxs quite often lately, and there they've used a custom return value called Subscription which can be called to remove the registered listener again. The same could be done in your case:

public interface Subscription {
    void unsubscribe();
}

Then change your addListener method to this:

public Subscription addListener(Consumer<String> listener) {
    listeners.add(listener);
    return () -> listeners.remove(listener);
}

The removeListener method can be removed entirely. And this can now be called like this:

Subscription s = subject.addListener(printListener::print);
// later on when you want to remove the listener
s.unsubscribe();

This works, because the returned lambda in addListener() still uses the same reference of listener and thus can again be removed from the List.
Side note: it would probably make more sense to use a Set unless you really care about the iteration order of your listeners

A nice read would also be Is there a way to compare lambdas?, which goes into more detail, why printListener::print != printListener::print.


回答

Your assumption is that each call to printListener::print returns the same instance of print.

Subject subject = new Subject();
PrintListener printListener= new PrintListener();
subject.add(printListener::print);
subject.remove(printListener::print);

The above code adds one listener, and tries to remove another listener, since printListener::print.equals(printListener::print) == false.

Subject subject = new Subject();
PrintListener printListener= new PrintListener();
Consumer<String> listener = printListener::print;
subject.add(listener);
subject.remove(listener);

The above works, requiring you to keep a reference to the listener if you need to remove it. Although if you wanted to keep it very lightweight, you could stick to only Consumers without the concrete PrintListener class, if the implementations are very simple.

Consumer<String> listener = System.out::println;
subject.add(listener);
subject.remove(listener);


以上是当它们作为lambda或方法引用传递时,如何实现可移除的侦听器?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>