Skip to main content

Properties in Java? Hoorah!

Posted by rbair on January 8, 2007 at 2:09 PM PST

Properties in Java? Awesome! As with any new language feature, there has been a lot of debate over whether this is an improvement to the language, or a detriment. And of course, every language-designer-wannabe (myself included!) is pounding the pulpit, declaring the One True Way to Property bliss. Well, sit back and enjoy as I pound the pulpit. Because seriously, I really do have the right solution! I promise!

A Brief Overview of JavaBeans

The JavaBeans spec has been with us for quite a while. Virtually everybody knows about the JavaBeans POJO pattern:

public class Person {
    private String surname;
    public String getSurname() {
        return surname;
    }
    public void setSurname(String surname) {
        this.surname = surname;
    }
}

Before throwing any monkey wrenches into the works, I'm going to explain a bit about some of the darker corners of the JavaBeans spec. At least, darker to the majority of developers out there.

Many of you are probably familiar with the Reflection APIs. For example, given the class above, I could call the "getSurname()" method using Reflection like so:

    Method m = Person.class.getMethod("getSurname");
    String surname = (String)m.invoke(personInstance);

Built above these core APIs is the JavaBeans Introspector, BeanInfo, PropertyDescriptor, and associated classes. These classes inspect an Object for methods that follow the JavaBean pattern, and create various descriptors automatically representing these "properties". You can even write your own PropertyDescriptors, or specify your own BeanInfos, if you don't want them to be automatically generated. Here's an example which get's all the properties for a bean and prints the name of the property and its value to System.out:

    BeanInfo info = Introspector.getBeanInfo(Person.class);
    for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
        String name = pd.getName();
        Method m = pd.getReadMethod();
        if (m != null) {
            System.out.println(name + "=" + m.invoke(personInstance));
        }
    }

The java.beans package is widely used by frameworks and GUI builders. Swing is a huge user of the Bean APIs and patterns. JSR 295 (beans binding) is based on these APIs as well.

There are a couple other important things to note. First, the class could be defined like this:

public class Person {
    public String getSurname() {
        return "";
    }
}

and it is still a valid JavaBean. The "surname" property still exists (it is just read only), and the Introspector still finds it. Even though there is no instance variable (aka field) called "surname". The JavaBeans pattern is not about breaking the encapsulation of the Person object. It is about exposing configurable state. JSR 273 will (if nothing major changes) introduce the ability to configure beans that are immutable (ie: only have getter methods, and accept all state in the constructor). So I repeat, the JavaBeans pattern is not about breaking encapsulation.

This is important for a couple of reasons. First, for the purists out there, you can stop wringing your hands. Second, there is a big difference between a property, and a field. A Property is a concept, a field is an implementation. When discussing language level property support, don't confuse the two!

Ground Rules

Any discussion centered around Property support has to first establish the ground rules: what are the requirements. Here are my requirements (pounding the pulpit as I say them):

  1. Any proposal must be backwards compatible with the existing JavaBeans spec!
  2. Leverage existing APIs and existing patterns where possible
  3. Introduce as little as possible to get the job done

The rest is all personal taste and other such nonsense :-).

The One True Way

Both Remi Forax and Cay Horstmann and Mikael Grev are on the right track. Each of those blogs has interesting ideas, and even more interesting commentary. Be sure to check those out.

The One True Way is not necessarily the way I'd do it if I were starting from a clean slate, but it is the best solution for Java. All design requires trade offs.Without further ado...

Getter/Setter == Accessor/Mutator

The first key design decision is to continue to support getter/setters as the way to customize the accessor/mutator for the property. C# uses a closure-like syntax for defining the accessor/mutator, but this would be the wrong decision for Java because it doesn't add anything truly useful beyond what the getter/setter already provide. Plus, as Remi points out, it limits your ability to use covariant return types or to override the argument to the setter.

Obviously, we can't ignore the last 10 years of Java code. Getter/Setter has to be supported.

PropertyDescriptor == Property Meta Data

The JavaBeans spec defines the PropertyDescriptor as the metadata for a Property. Stephen Colebourne has an interesting idea regarding exposing Property meta information easily. He has some interesting points, but it is critical that we leverage the existing APIs and the existing body of work that uses PropertyDescriptor. However, we really, really, should make it easier to get a PropertyDescriptor.

For example, Stephen suggests the "->" notation (which has been suggested for other uses with regards to properties, so don't get confused!). Basically, foo.bar would return the value of the bar property, while foo->bar would yield the PropertyDescriptor. I'm not fond of the use of the "->" operator for this task, but something along these lines may be the best choice.

Observable Properties

The JavaBeans specification mentions "bound properties", or in other words, observable properties. Whenever a "bound" property changes, a PropertyChangeEvent is fired to all registered PropertyChangeListeners. They may then take the correct action.

This is a huge deal for JSR 295 (bean binding). Using my previous code example, imagine if I bound the Person.surname property to a JLabel. Whenever the surname property changes, I want to update the label. If the surname property is not observable, then the JLabel will not be automatically notified when the property changes. Which means you'd have to be sure to manually refresh the JLabel whenever the surname changes. Lame!

Writing a setter method to fire a PropertyChangeEvent isn't too hard, but it is trickier than you might think! Here's a broken example:

    public void setSurname(String surname) {
        if (surname == null) surname = "";
        String old = this.surname;
        this.surname = surname;
        firePropertyChange("surname", old, surname);
    }
    public String getSurname() {
        return surname;
    }

    ...

    final Person p = ...;
    p.setSurname("Bair");
    p.addPropertyChangeListener(new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent evt) {
            assert p.getSurname().equals(evt.getNewValue());
        }
     }

What is wrong with this example? Will the assert always succeed? Answer, no. It may fail. How? Simple: because getSurname() is not final, it is possible for a subclass to be written such that it always returns a single value, essentially becoming read only. That is:

    public class SmithPerson extends Person {
        public String getSurname() { return "Smith"; }
    }

    ...

    final Person p = new SmithPerson();
    p.setSurname("Bair"); // set surname sets the field to be "Bair", fires a PCE
    p.addPropertyChangeListener(new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent evt) {
            //evt.getNewValue() returns "Bair", but p.getSurname() returns "Smith"!
            assert p.getSurname().equals(evt.getNewValue());
        }
     }

A more correct version of the setSurname() method would be

    public void setSurname(String surname) {
        if (surname == null) surname = "";
        String old = getSurname(); // note: calling the getter!
        this.surname = surname;
        firePropertyChange("surname", old, getSurname()); // still calling the getter!
    }

Or, just make the getter final.

The point is, writing an observable setter has some gotchas. It's boring. It's a bunch of boilerplate. It is necessary for really slick (and useful and powerful) data binding. This has to be easier to do! Any talk about including properties in the language must include a provision for dealing with observable properties.

Note that the JavaBeans spec also discusses vetoable property change events, but those are, in my estimation, less critical to handle at this level. At least, whatever mechanism is introduced for observable properties should be capable of extension towards vetoable properties.

Code Generation Rules

Actually, I have reservations on this point, but they are outweighed by practicality. That is, however a property is defined it must actually generate the getter/setter method in the end, so as to be compatible with the JavaBeans spec. However, this is kinda weird. I mean, you define a property, and then call a method "getSurname()". Where was that method defined? "Oh, the compiler creates it for you". I mean, that's just plain freaky.

However, it is the most practical choice at the moment, in my estimation.

Code generation is the means by which we ensure that properties work with existing frameworks and code. A piece of code calling Introspector.getBeanInfo should work exactly the same whether it is introspecting a class defined pre 1.7 or a class defined with properties.

Things To Postpone

One really intriguing idea is to also introduce a way to chain accessors, such that if a null occurs anywhere in the chain, the result is a null. For example, a.getB().getC() would return null instead of throwing a NullPointerException if a.getB() returned null. Some syntax changes have been suggested (such as using a # instead of . to access the property). Very interesting idea, could save a lot of boilerplate, but this is orthogonal to the properties discussion. At least, I hope it is. The death of many a great idea has come about due to too many great ideas being lumped together.

The End of Today's Blog

So, I haven't actually proposed any syntax of any kind. Rather, I'm trying to focus on, and nail down, the core requirements for language level properties, from my perspective. In this discussion, I'm not acting as the official Sun representative for Properties in the language. I'm just an interested bystander. So look to other blogs for official feedback, direction, etc etc.

Related Topics >>