Friday, March 09, 2007

Java properties with no language changes (oh no!)


There's been a lot of discussions on the web about Java and the support for bean properties. People would like to have a mechanism that relies less on java bean conventions (reflection on method names, find getters and setters) and more on explicit language constructs. The goal is to simplify bean and property handling, have stronger type checks, be able to reference the property and not its values, etc... Most proposals involve additions to the Java syntax. I don't want to add to the confusion, but I just played around with what we have now in Java 5.
What we could have is instead of defining classes directly with fields in them, we could have them reference Property objects instead. Property objects carry the type information and the value itself. An inheritance hierarchy can provide for read-only, write-only and read-write hierarchy. Using public final fields we can have an access syntax that is not the same as the current getter and setter methods, but pretty close.
Example:

public class PersonBean {
public final IFullProperty name=new NonNullProperty();

public final IReadProperty id;

public final IFullProperty email=new FullProperty(){
@Override
public void set(String value) {
if (value.indexOf('@')==-1){
throw new IllegalArgumentException("email does not contain @");
}
super.set(value);
}
};

public PersonBean(int id,String name){
this.name.set(name);
this.id=new ReadOnlyProperty(id);
}
}

Of course, the generic madness means the type information has to be typed twice, but when generics syntax is cleaned up it'd be a lot more readable. This bean says that it has a changeable name property, a String that cannot be null. It has an id integer property that cannot be changed. Finally it has an email property that has ad hoc constraint checking. The Property interfaces and its sub interfaces define a get() and a set() method so accessing the fields becomes:

bean.getName() -> bean.name.get()
bean.setName(name) -> bean.name.set(name)

The property change listener can be attached to the property object so that the bean doesn't have to have any line of code related to listeners. Everything can be done via static methods:

static void addPropertyChangeListener(Object bean,PropertyChangeListener pcl);

to add a listener on all properties


static void addPropertyChangeListener(Object bean,PropertyChangeListener pcl,IProperty p);

to add a listener on a specific property. Note that since we're taking a property object, and not a property name for example we cannot register a listener on a non existing property. The property does not have a back link to its bean (to avoid disgracious constructors with this), so we need to have both the bean and the propert.
Example:
addPropertyChangeListener(bean,listener,bean.email)

If anybody's interested, all the code with unit tests is available here.




2 comments:

Oisin G. said...

Sounds like you should just switch to C# Jp! ;-)

JP Moresmau said...

Except that here you can reference the property object, not only its values, so you can pass it to a method that can both read and set its values, whereas if I'm not mistaken, in C# you can get or set the value but not pass a reference to the property itself.