I am wondering what may be a good way of implementing some kind of observable in Java without much interface-stuff.
I thought it would be nice to use the predefined functional interfaces. For this example, I use a String
Consumer
to represent a listener that takes a String for notification.
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
I found the explanation:
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
So I can't use lambdas/function pointers as they are.
There is always the alternative to have the good old interfaces back, s.t. we register object instances but not lambdas. But i hoped there is something more lightweight.
EDIT:
From current responses, I see the following approaches:
a) Return a Handle
that holds the original reference
b) store the original reference yourself
c) Return some ID (integer) that can be used in subject.remove()
instead of the original reference
I tend to like a). You still have to keep track of the 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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With