Thursday, 27 September 2007

Java 7 - Update on Properties

Whilst I was on holiday there was a bit of discussion about properties in Java.

Shannon Hickey announced Beans Binding version 1.0. This is the JSR that intends to develop an API to connect together swing components in a simple and easy to use manner. The idea is that you call a simple API to setup a binding between a GUI field and your data model.

During development, a lot of discussion on the JSR was about the interaction with the possible language proposal for properties. As a result, the v1.0 API includes a Property abstract class, with a subclasses for beans and the EL language from J2EE.

Per-class or per-instance

One key issue that has to be addressed with a property proposal is whether to go per-class or per-instance. The per-class approach results in usage like Method or Field, where the bean must be passed in to obtain the value:

 Person mother = ...;
 Property<Person, String> surnameProperty = BeanProperty.create("surname");
 String surname = surnameProperty.getValue(mother);

Here, the surname property refers to the surname on all Person beans. The caller must pass in a specific person instance to extract the value of the surname.

On the other hand, the per-instance approach results in usage where the property itself knows what bean it is attached to:

 Person mother = ...;
 Property<Person, String> surnameProperty = BeanProperty.create("surname", mother);
 String surname = surnameProperty.getValue();

Here, the surname property refers to the specific surname property on the mother instance. There is thus no need to pass in any object to extract the value of the surname, as the property knows that it is attached to mother.

Which is better? And which did Beans Binding adopt?

Well, its actually the case that for the swing use case, the per-class approach is really the only option. Consider a table where each row is the representation of a bean in a list. The columns of this table are clearly going to be properties, but which type? Well, when defining the binding, we want to do this without any reference to the beans in the list - after all, the beans may change and we don't want to have to redefine the binding if they do.

So, we naturally need the per-class rather than per-instance properties. And, that is what the Beans Binding project defines.

Is there ever a need for the per-instance approach? In my opinion yes, but that can be added to the per-class based property functionality easily.

Adding language support

Having seen the Beans Binding release, Remi Forax released a version of his property language compiler that integrates:

 Person mother = ...;
 Property<Person, String> surnameProperty = LangProperty.create(Person#surname);
 String surname = surnameProperty.getValue(mother);

As can be seen, the main advantage is that we now have a type-safe, compile-time checked, refactorable property Person#surname. This is a tremendous leap forward for Java, and one that shouldn't be underestimated. And the # syntax can easily be extended to fields and methods (FCM).

But, why do we see this new class LangProperty? Well, Remi's implementation returns an object of type java.lang.Property, not a Beans Binding org.jdesktop.Property. This fustrating difference means that two classes named Property exist, and conversion has to take place.

What this really demonstrates is the need for Sun to declare if Java 7 will or won't contain a language change for properties, and for a real effort to occur to unify the various different JSRs currently running in the beans/properties arena. It would be a real disaster for Java to end up with two similar solutions to the same problem (although I should at least say I'm very happy that the two parts are compatible :-).

Summary

Properties support in Java is progressing through a combination of JSR and individual work. Yet Sun is still silent on Java 7 language changes. Why is that?

Opinions welcomed as always!

10 comments:

  1. It would be great news if Java 7 would include properties support. The # syntax seems just fine. I really hate having strings for properties (the main reason being that no IDE is smart enough to handle them with refactoring or even a simple "find usage" case).

    ReplyDelete
  2. Thanks for the brilliant recap, Stephen. I'm still trying to wedge in a few more changes to the existing Property abstract class. These people - they love abstract classes for some reason! I don't understand it.

    ReplyDelete
  3. I'd like that the syntax allowed the . operator instead. Whats the horrible thing about LangProperty.create(Person.surname), field aliasing? Just develop an hiearchy, and a warning:
    Warning method-field alising using a property, using the method...
    Anyway fields with the same name as methods are LAME, and should have been forbidden. Don't introduce operators for no reason at all.

    ReplyDelete
  4. Hello Stephen,

    I have created a property proposal (not public yet) and it is using the per instance approach. I have heard Jesse say that thing about the JTable should be a problem but no one has explained why. I can't imagine why a connection to the bean (JTable) should change anything. Can you explain?

    ReplyDelete
  5. Just in case you missed it, the debate over a syntax for compile-time checked member access (not just properties) is old enough:

    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4810162

    ReplyDelete
  6. Stephen Colebourne28 September 2007 at 10:21

    @i30817, I prefer, as do others, adding the # operator simply for the additional clarity. Person.surname looks like it is accessing the surname field which returns a String. What we want here is to return the *meta* information about the surname, the Property object, hence Person#surname.

    @svenmeier, Thanks for the link.

    @Mikael, Bindings can be added to the JTable before there any model beans have been created. The list of model beans may grow or shrink. You don't want to link the actual model bean to the column, you just want to link the fact that the surname should be extracted from each model bean.

    Re-reading your comment, you ask why a link to the JTable bean would have an effect. It doesn't, and thats not the reason why per-instance doesn't work. The reason is linked to the model beans (Person etc.).

    ReplyDelete
  7. Stephen Colebourne28 September 2007 at 10:38

    A better Sun RFE link is http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5043025

    ReplyDelete
  8. Stephen, are you assuming the connection to a bean can not be changed once set? Of course it can. You can create 'per-instance' JTable property where the JTable/model is settable with a setBean() method. At least this is how I think of it.

    Btw, IMO a property that is per-class is actually a PropertyAdapter, not a Property.

    Cheers,

    ReplyDelete
  9. Stephen Colebourne28 September 2007 at 17:29

    @Mikael, I believe that people use the name 'Property' to be per-class is that it matches with 'Field' and 'Method'. The whole set are at the meta-level. Thus we need to find a different name for the instantiated property holding the actual value, perhaps PropertyAccessor or PropertyInstance.

    As for the table column, IMO it should ideally be a binding between the property instance for the table column, and the (meta) property for the data.

    bind(myTableColumn#value, Person#surname);

    As can be seen, the column is lined to an instance, while the data model is just referring to the surname property on any and all Person objects.

    ReplyDelete
  10. Why not
    Person mother = ...;
    PropertyDelegate surnamePropertyDelegate = BeanProperty.create("surname");
    String surname = surnamePropertyDelegate.getValue(mother);

    versus

    Person mother = ...;
    Property surnameProperty = BeanProperty.create("surname", mother);
    String surname = surnameProperty.getValue();

    and thus

    surnamePropertyDelegate.getProperty(mother).getValue()

    ReplyDelete