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

John Hendrikx john.hendrikx at gmail.com
Thu Jan 19 15:49:31 UTC 2023


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