3 Replies Latest reply: May 17, 2012 6:41 AM by JohnHendrikx RSS

    Why no lazy binding?

    JohnHendrikx
      With all the talk about the differences between ChangeListeners and InvalidationListeners, I thought binding one property to another would make the bound property invalid until it was actually used... so I was kind of surprised this prints "Get was called":
      import javafx.beans.property.SimpleStringProperty;
      import javafx.beans.property.StringProperty;
      
      public class BindTest {
        public static void main(String[] args) {
          StringProperty p = new SimpleStringProperty("Hi") {
            @Override
            public String get() {
              System.out.println("Get was called");
              return super.get();
            }
          };
      
          StringProperty q = new SimpleStringProperty();
      
          q.bind(p);
        }
      }
      Shouldn't this wait until I actually try accessing 'q'?

      This is rather annoying as property 'p' in my program is a lazily database fetched property that should only be fetched when it is REALLY needed... not when it has just got bind() called on it...
        • 1. Re: Why no lazy binding?
          JohnHendrikx
          What's worse... this has same effect, so there is not even an easy work-around:
          import javafx.beans.InvalidationListener;
          import javafx.beans.Observable;
          import javafx.beans.property.SimpleStringProperty;
          import javafx.beans.property.StringProperty;
          
          public class BindTest {
            public static void main(String[] args) {
              StringProperty s = new SimpleStringProperty("Hi") {
                @Override
                public String get() {
                  System.out.println("Get 2 was also called");
                  return super.get();
                }
              };
          
              s.addListener(new InvalidationListener() {
                @Override
                public void invalidated(Observable observable) {
                  System.out.println("The observable changed");
                }
              });
            }
          }
          It only prints: "Get 2 was also called".
          • 2. Re: Why no lazy binding?
            895058
            An Observable gets validated when you attach a Listener. That is why the getter gets called in your examples.

            The first versions of the property and binding API did not do that. At that time many developers attached a listener to an invalid property and were surprised that they did not get any notifications. Naturally they thought the listener functionality was broken. Sure, after explaining it, they understood. But after closing the x-th bug report stating that listeners are broken, we decided that it is probably better to change the behavior - even if that means that we do one unnecessary get() call.

            I am afraid the only really safe solution is to write your own implementation of StringProperty.

            There is another, simpler solution, but it is only a hack, not future proof, and should only be used in non productive environments. In other words, I wil not explain it in a public forum. :-) You can contact me directly, if you are interested.
            • 3. Re: Why no lazy binding?
              JohnHendrikx
              That's a shame, I'm using a work-around now that's basically an artifact of the fact that you can basically copy properties.

              My use case is that I have an object that provides JavaFX style properties; these properties when accessed trigger a background process to fetch more detailed information (for example, the background image property initially will be 'null', but when you access it it will start fetching the data for this in the background -- once it is done the property will be updated and anyone listening to it will then update the background image from 'null' to an Image object).

              However, the system got a bit more complicated and I've decided to make some of these properties available in a 2nd object and since many of them are simply links to this first object I just recreated the properties and bound some of them in this 2nd object. However, the act of binding causes the background fetching behaviour to be triggered, which is highly undesirable as this should only be done sparingly for objects actually having a visual presence (ie, part of the Scene).

              Now, to work around this, instead of binding I just copied the properties... like this stripped down example:
              public SecondaryObject {
                private ObjectProperty<Image> backgroundImage;
              
                public SecondaryObject(PrimaryObject po) {
                   // backgroundImage.bind(po.backgroundImageProperty());
                   backgroundImage = po.backGroundImageProperty();
                }
              }
              This causes no validation... however it leaves my unhappy as it feels wrong to steal properties like this from other objects. It also may cause lifecycle issues as objects binding to this backgroundImage property are now binding themselves to the long-lived primary object without them knowing about it (the plan was to only bind weakly from the secondary object to the primary object).

              I've moved on to something else for now, but this is something I will need to fix in the future so I'm exploring my options.

              A possible solution might be to delay binding to the primary object until someone binds to the secondary object... hm :)