Feedback requested for infrastructure for properties that wish to delay registering listeners

John Hendrikx john.hendrikx at gmail.com
Tue Feb 7 14:43:08 UTC 2023


I've made a draft PR which shows the changes I'm proposing below.

For those interested, it can be found here: 
https://github.com/openjdk/jfx/pull/1023

--John

On 19/01/2023 16:49, John Hendrikx wrote:
> Hi list,
>
> I've been looking into what it would take to make the design of 
> properties which only register listeners when needed easier to 
> implement with JavaFX, both for JavaFX itself as well as for the 
> user.  This is the result of my investigation into ListProperty (and 
> other collection properties) and how hard it would be to change their 
> semantics to only register listeners when they're absolutely required 
> for the correct functioning of the class.
>
> Currently, JavaFX doesn't offer the tools one would need to register 
> listeners in a just-in-time fashion.  This is because there is no 
> notification mechanism available in its observable values when they 
> become observed or unobserved. The closest thing there currently 
> exists is what ObjectBinding and LazyObjectBinding offer:
>
> From ObjectBinding:
>
>     /**
>      * Checks if the binding has at least one listener registered on 
> it. This
>      * is useful for subclasses which want to conserve resources when 
> not observed.
>      *
>      * @return {@code true} if this binding currently has one or more
>      *     listeners registered on it, otherwise {@code false}
>      * @since 19
>      */
>     protected final boolean isObserved() {
>         return helper != null;
>     }
>
> From LazyObjectBinding:
>
>     /**
>      * Called after a listener was added to start observing inputs if 
> they're not observed already.
>      */
>     private void updateSubscriptionAfterAdd() { ... }
>
>     /**
>      * Called after a listener was removed to stop observing inputs if 
> this was the last listener
>      * observing this binding.
>      */
>     private void updateSubscriptionAfterRemove() { ... }
>
> I'm proposing to extend the JavaFX with new methods that can be used 
> to stay informed about the observable's current observed status. These 
> methods will facilitate implementations of properties that wish to 
> delay registering listeners until they are themselves observed. This 
> can save memory, save unnecessary notifications via events and reduces 
> the need for weak listener constructs.
>
> The proposal does not require any new fields, and would default to 
> reporting observable values as being observed. Most JavaFX observable 
> values should be retrofitted to support this functionality -- the ones 
> relying on a form of ExpressionHelper will only require minimal 
> changes with minimal impact.
>
> The methods that I would like to see added are the following:
>
> In ObservableValue:
>
>     /**
>      * Checks if this ObservableValue is currently observed. If 
> unknown or unsupported,
>      * {@code true} is returned.
>      *
>      * @return {@code true} if this ObservableValue currently has one 
> or more
>      *     listeners registered on it, otherwise {@code false}
>      */
>     default boolean isObserved() { return true; }
>
> The above method is useful for debugging, but its primary use is for 
> complex properties which observed status is determined by not only its 
> direct listeners but also any child properties it may offer.  
> ListProperty is such an example, which offers child properties size 
> and empty. Its observed status would be determined like this:
>
>      helper != null || (size0 != null && size0.isObserved()) || 
> (empty0 != null && empty0.isObserved())
>
> Further, we need two protected callback methods.  These should be 
> added in all Binding and Property Base classes (basically all classes 
> that implement addListener/removeListener methods). These are called 
> when the observed status of the property changes:
>
>     /**
>      * Called when this property transitions from unobserved to observed.
>      */
>     protected void observed() {}
>
>     /**
>      * Called when this property transitions from observed to unobserved.
>      */
>     protected void unobserved() {}
>
> These methods are implemented by child properties in order to inform 
> the parent that their observed status has changed, which may require 
> the parent property to change its status as well.
>
> When implemented, LazyObjectBinding can be simplified as some or all 
> of its functionality will be part of ObjectBinding (LazyObjectBinding 
> is not a public class at the moment).  The isObserved method in 
> ObjectBinding would go from protected to public (it's already final).
>
> Implementation for classes that rely on a form of ExpressionHelper is 
> simple; they can check if the helper is null or not (for isObserved) 
> and check if the nullity changed during addListener/removeListener 
> calls to make their call to "observed" or "unobserved".  No additional 
> fields are required.
>
> I've added a proof of concept here 
> (https://github.com/openjdk/jfx/pull/1004) where `ListPropertyBase` 
> was altered to use these new methods to delay its listener 
> registration and to remove its listener when no longer observed. This 
> PoC includes the tests written by Florian Kirmaier which fail on the 
> current ListProperty implementation (but pass with this version).
>
> --John
>


More information about the openjfx-dev mailing list