On Mon, Mar 24, 2014 at 3:24 PM, Mario Ivankovits <ma...@datenwort.at> wrote:
> Hi Tomas!
>
> No worries, I’ll go the way on my own there then.
>
>
> But, after this discussion I do not see why one ever used .equals() at all.
>
> Look, it does not fit my needs, I do not see any use-case where one would add 
> an removeListener with asymmetric .equals() and thus it is better you use == 
> I think.

As Martin pointed out, sometimes == doesn't work for you and you need
(symmetric) equals(). Another example is my proposed solution #2 to
your problem.
Also, using equals instead of identity comparison is consistent with
how Collection.remove() determines equality.

> This clarifies that EXACTLY the added listener instance is required to remove 
> any listener AND it gives no room to discussions like we had because the 
> indention is perfectly clear - even to those reading JavaFX core code and bug 
> fixing things in JavaFX.
> For me this would be fine and understandable - I will go the 
> decorator-cache-map so I will be fine always.
>
>
> BTW: There are always implementation details one has to assume. For example, 
> I have to assume that the internals will always use the add/removeListener 
> methods and not modifying the list (or whatever) directly.
> There is something which we need to hold on - even if there is no 
> documentation for things like this.
> Any yay, if you patch some JavaFX core stuff, you will get deeper insights 
> and do not need to assume anymore, you KNOW how it works. Sometimes it is not 
> easy to misuse things then to get your job done! ;-)
>
>
> Also, if there is a risk in there current implementation it does not help 
> killing the discussion with the argument that there are other risks too.
> I am pretty well aware about stuff like that.
>
>
> PS: I would love to explain in detail why I decorate change listener, but 
> this is out of scope of this thread.
>
> Regards,
> Mario
>
>
> Am 24.03.2014 um 14:04 schrieb Tomas Mikula <tomas.mik...@gmail.com>:
>
>> Hi Mario,
>>
>> On Mon, Mar 24, 2014 at 8:46 AM, Mario Ivankovits <ma...@datenwort.at> wrote:
>>> Thanks for your answer!
>>>
>>> One thing, I think, we can agree on, is that it is overly complex for an OO 
>>> language to decorate things like these listeners.
>>
>> I'm curious why you need to decorate the listeners in the first place.
>>
>>> What about introducing another interface like ChangeListenerDecoration with 
>>> a special named „boolean decorates(ChangeListener)) method and call this if 
>>> the listeners hold by *ExpressionHelp class implements that interface?
>>
>> If you are writing your own ObservableValue implementation, you can as
>> well implement your own ExpressionHelper that does whatever you want
>> (just that you don't forget this option ;-)).
>>
>>> BTW: If we care about symmetry, why not change the 
>>> listener.equals(otherListener) to listener == otherListener at all? Then, 
>>> there are not implementation details one can make use of.
>>> I even might argue that it is a security risk to use the .equals() the way 
>>> it is now, as an evil listener implementation is able to remove all 
>>> listeners from the list by simply returning true from equals()
>>> It should be the responsibility of the listener in the list to know if the 
>>> passed in listener justify the removal of itself, right? … from a security 
>>> standpoint! :-)
>>
>> You cannot secure your application at this level. An "evil" listener
>> might as well introduce memory leaks, cause stack overflow, or wipe
>> out your hard disk.
>>
>> Tomas
>>
>>>
>>> Regards,
>>> Mario
>>>
>>>
>>> Am 24.03.2014 um 07:31 schrieb Martin Sladecek <martin.slade...@oracle.com>:
>>>
>>>> Mario,
>>>> thanks for your suggestion, but I think your specific case will not 
>>>> justify the change. First of all, as it was already said, equals should be 
>>>> symmetric. Knowing the implementation, you could rely on the fact that 
>>>> either the equals is called on the provided listener or on the listeners 
>>>> already in the list. Might be that somebody else would need the asymmetry 
>>>> to be implemented in the "listener" provided to removeListener() method 
>>>> and the current situation suits him, so why do the change for your case?
>>>>
>>>> Anyway, relying on some implementation-specific behaviour is generally a 
>>>> sign of bad code practice, so it should give you a hint that you should 
>>>> find a different solution (like the one you discussed with Tomas already). 
>>>> It might happen that the code would be changed / re-factored in the future 
>>>> or some bug would be fixed there and behaviour will change again, breaking 
>>>> your application in some future JFX version.
>>>>
>>>> Regards,
>>>> -Martin
>>>>
>>>>
>>>> On 22.3.2014 15:47, Mario Ivankovits 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<mailto: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><mailto: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><mailto: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><mailto: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
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>
>

Reply via email to