On Sat, Mar 22, 2014 at 4:11 PM, Mario Ivankovits <ma...@datenwort.at> wrote: > Probably you avoid the map, but it requires to decorate the listener again, > right?
Right. For a pair of addListener-removeListener calls, you have 2 decorations instead of 1. I usually think of the costs in asymptotic terms: if 1 decoration is cheap, then 2 decorations are cheap as well. Or, if we can afford n decorations, then we can probably afford 2n decorations. I know this is not quite true in real world (200ms is not the same as 100ms), but is a good way to think about complexity. > If you think about the tons of observable values and add/removeListener > operations in an tree view when scrolling it might get costly and put > pressure on the GC. removeListener creates one extra short-lived object. I don't know if it is still true for the current garbage collector in Hotspot, but a while ago I read that creating a lot of cheap short-lived objects is fine for the GC. Regards, Tomas > > Anyway, I will be more than happy to assist with a cleaner solution by > introduction an interface like DecoratedChangeListener which provides a > special .equalsChangeListener(ChangeListener other) method. > > > Am 22.03.2014 um 15:55 schrieb Tomas Mikula <tomas.mik...@gmail.com>: > > Hi Mario, > > even if your proposal gets accepted, you would still depend on an > implementation detail of JavaFX, which is always good to avoid. > > To sum up, my second proposal compared to your current solution: > > (+) your equals() stays symmetric; > (+) no need for an extra Map of listeners. > > My second proposal compared to your desired solution if your proposal > is accepted: > > (+) your equals() stays symmetric; > (+) you don't depend on an implementation detail in JavaFX. > > Best, > Tomas > > On Sat, Mar 22, 2014 at 3:47 PM, Mario Ivankovits <ma...@datenwort.at> > wrote: > > The only thing which I ask for is to flip this „if" in the *ExpressionHelper > classes: > So, JavaFX does not break anything and it is up to the app developer to take > the risk or not. > > if (listener.equals(changeListeners[index])) { > > If we flip this to > > if (changeListeners[index].equals(listener)) > > > > > Am 22.03.2014 um 15:42 schrieb Kevin Rushforth <kevin.rushfo...@oracle.com>: > > If you are talking about a change to the JavaFX API, we are not likely to > accept such a change if it breaks the contract of equals() or hashcode(). In > your own app it may not matter, but it is dangerous to assume it won't > matter to anyone. Martin owns the core libraries and can comment further. > > -- Kevin > > > Mario Ivankovits wrote: > > Hi Thomas! > > Thanks for your input. Because I want to decorated listeners added by JavaFX > core I can not use the sub/unsub pattern. > Your second proposal is almost what I do right now. In removeListener I > consult a map where the decorated listeners and their undecorated one lives. > > Regarding the symmetric doubts. Such listeners will always be removed by > passing in the undecorated object to removeListener. > They should act like a transparent proxy. > > Even if this breaks the equals paradigm, in this special case I can > perfectly live with an equals/hashCode implementation like this below. > It won’t break your app; as long as both objects do not live in the same > HashSet/Map …. for sure - but why should they? > > @Override > public boolean equals(Object obj) > { > return obj == delegate; // && this == obj > } > > @Override > public int hashCode() > { > return delegate.hashCode(); > } > > > Regards, > Mario > > > Am 22.03.2014 um 14:22 schrieb Tomas Mikula <tomas.mik...@gmail.com>: > > > > A simpler and more universal solution is to also override removeListener: > > public void removeListener(ChangeListener<? super T> listener) { > super.removeListener(decorated(listener)); > } > > And the equals() method on decorated listeners only compares the > delegates (thus is symmetric). > > Regards, > Tomas > > > On Sat, Mar 22, 2014 at 2:07 PM, Tomas Mikula <tomas.mik...@gmail.com> > wrote: > > > The suspicious thing about your solution is that your smart > implementation of equals() is not symmetric. > > In case the observable value is visible only within your project, you > could do this: > > interface Subscription { > void unsubscribe(); > } > > class MyObservableValue<T> implements ObservableValue<T> { > public Subscription subscribe(ChangeListener<? extends T> listener) { > ChangeListener<T> decorated = decorate(listener); > addListener(decorated); > return () -> removeListener(decorated); > } > } > > and use subscribe()/unsubscribe() instead of addListener()/removeListener(): > > Subscription sub = observableValue.subscribe(listener); > // ... > sub.unsubscribe(); > > Of course this is not possible if you need to pass the observable > value to the outside world as ObservableValue. > > Regards, > Tomas > > On Sat, Mar 22, 2014 at 6:57 AM, Mario Ivankovits <ma...@datenwort.at> > wrote: > > > Hi! > > In one of my ObservableValue implementations I do have the need to decorate > ChangeListener added to it. > Today this is somewhat complicated to implement, as I have to keep a map of > the original listener to the decorated one to being able to handle the > removal process of a listener. Because the outside World did not know that I > decorated the listener and the instance passed in to removeListener ist the > undecorated one. > > We can make things easier with a small change in > ExpressionHelper.removeListener. When iterating through the listener list, > the listener passed in as argument to removeListener is asked if it is equal > to the one stored > > if (listener.equals(changeListeners[index])) { > > If we flip this to > > if (changeListeners[index].equals(listener)) { > > a listener decoration can be smart enough to not only check against itself, > but also against its delegate. > > What do you think? > > I could prepare a patch for the *ExpressionHelper classes. > > > Best regards, > Mario > >