Fork us on GitHub

Properties

Rethinking POJO's and property mapping in Codename One and beyond
Post Image

Properties

We usually just add a new feature and then tell you about it in these posts but properties is a special case and this post is intended not just as a tutorial but as a solicitation of feedback…​
We committed properties as a deprecated API because we aren’t sure yet. This could be a very important API moving forward and we want as much peer review as possible over this.

What are Properties?

In standard Java we usually have a POJO (Plain Old Java Object) which has getters/setters e.g. we can have a simple Meeting class like this:

public class Meeting {
     private Date when;
     private String subject;
     private int attendance;

     public Date getWhen() {
        return when;
    }

     public String getSubject() {
        return subject;
     }

     public int getAttendance() {
        return attendance;
     }

     public void setWhen(Date when) {
        this.when = when;
    }

     public void setSubject(String subject) {
        this.subject  = subject;
     }

     public void setAttendance(int attendance) {
        this.attendance = attendance;
     }
}

That’s a classic POJO and it is the force that underlies JavaBeans and quite a few tools in Java.

The properties are effectively the getters/setters e.g. subject, when etc. but properties have several features that are crucial:

  • They can be manipulated in runtime by a tool that had no knowledge of them during compile time

  • They are observable - a tool can monitor changes to a value of a property

  • They can have meta-data associated with them

These features are crucial since properties allow us all kinds of magic e.g. hibernate/ORM uses properties to bind Java objects to a database represenation, jaxb does it to parse XML directly into Java objects and GUI builders use them to let us customize UI’s visually.

POJO’s don’t support most of that so pretty much all Java based tools use a lot of reflection & bytecode manipulation. This works but has a lot of downsides e.g. say I want to map an object both to the Database and to XML/JSON. Would the bytecode manipulation collide?

Would it result in duplicate efforts?

And how do I write custom generic code that uses such abilities? Do I need to manipulate the VM?

Properties in Java

These are all very abstract ideas, lets look at how we think properties should look in Java and how we can benefit from this moving forward.

The code below is preliminary and the syntax/classes might change without warning

This is the same class as the one above written with properties:

public class Meeting implements PropertyBusinessObject {
     public final Property<Date,Meeting> when = new Property<>("when");
     public final Property<String,Meeting> subject = new Property<>("subject");
     public final Property<Integer,Meeting>  attendance = new Property<>("attendance");
     private final PropertyIndex idx = new PropertyIndex(this, "Meeting", when, subject, attendance);

    @Override
    public PropertyIndex getPropertyIndex() {
        return idx;
    }
}

This looks a bit like a handful so let’s start with usage which might clarify a few things then dig into the class itself.

When we used a POJO we did this:

Meeting meet = new Meeting();
meet.setSubject("My Subject");
Log.p(meet.getSubject());

With properties we do this:

Meeting meet = new Meeting();
meet.subject.set("My Subject");
Log.p(meet.subject.get());

Encapsulation

At first glance it looks like we just created public fields (which we did) but if you will look closely at the declaration you will notice the final keyword:

     public final Property<String,Meeting> subject = new Property<>("subject");

This means that this code will not compile:

meet.subject = otherValue;

So all setting/getting must happen thru the set/get methods and they can be replaced. E.g. this is valid syntax that prevents setting the property to null and defaults it to an empty string:

public final Property<String,Meeting> subject = new Property<>("subject", "") {
     public Meeting set(String value) {
         if(value == null) {
            return Meeting.this;
         }
         return super.set(value);
     }
};
We’ll discuss the reason for returning the Meeting instance below

Introspection & Observability

Since Property is a common class it’s pretty easy for introspective code to manipulate properties. However, it can’t detect properties in an object without reflection.

That’s why we have the index object and the PropertyBusinessObject interface (which defines getPropertyIndex).

The PropertyIndex class provides meta data for the surrounding class including the list of the properties within. It allows enumerating the properties and iterating over them making them accessible to all tools.

Furthermore all properties are observable with the property change listener. I can just write this to instantly print out any change made to the property:

meet.subject.addChangeListener((p) -> Log.p("New property value is: " + p.get()));

The Cool Stuff

That’s the simple stuff that can be done with properties, but they can do much more!

For starters all the common methods of Object can be implemented with almost no code:

public class Meeting implements PropertyBusinessObject {
     public final Property<Date,Meeting> when = new Property<>("when");
     public final Property<String,Meeting> subject = new Property<>("subject");
     public final Property<Integer,Meeting>  attendance = new Property<>("attendance");
     private final PropertyIndex idx = new PropertyIndex(this, "Meeting", when, subject, attendance);

    @Override
    public PropertyIndex getPropertyIndex() {
        return idx;
    }

    public String toString() {
        return idx.toString();
    }

    @Override
    public boolean equals(Object obj) {
        return obj.getClass() == getClass() && idx.equals(((TodoTask)obj).getPropertyIndex());
    }

    @Override
    public int hashCode() {
        return idx.hashCode();
    }
}

This is easy thanks to introspection…​

We already have some simple code that can convert an object to/from JSON Maps e.g. this can fill the property values from parsed JSON:

meet.getPropertyIndex().populateFromMap(jsonParsedData);

And visa versa:

String jsonString = meet.toJSON();

We also have a very simple ORM solution that maps values to table columns and can create tables. It’s no hibernate but sqlite isn’t exactly big iron so it might be good enough.

Constructors

One of the problematic issues with constructors is that any change starts propagating everywhere. If I have fields in the constructor and I add a new field later I need to keep the old constructor for compatibility.

So we added a new syntax:

Meeting meet = new Meeting().
        subject.set("My Subject").
        when.set(new Date());

That is why every property in the definition needed the Meeting generic and the set method returns the Meeting instance…​

We are pretty conflicted on this feature and are thinking about removing it.

Without this feature the code would look like this:

Meeting meet = new Meeting();
meet.subject.set("My Subject");
meet.when.set(new Date());

Is this feature valuable?

Is it worth the cost of converting this:

public class Meeting implements PropertyBusinessObject {
     public final Property<Date> when = new Property<>("when");
     public final Property<String> subject = new Property<>("subject");
     public final Property<Integer>  attendance = new Property<>("attendance");

To this:

public class Meeting implements PropertyBusinessObject {
     public final Property<Date,Meeting> when = new Property<>("when");
     public final Property<String,Meeting> subject = new Property<>("subject");
     public final Property<Integer,Meeting>  attendance = new Property<>("attendance");

I’m personally conflicted here…​

Feedback & Summary

The reason for this post is feedback, we’d like feedback on all of the above and general thoughts on this.

Is this something you are interested in?

What features are important to you?

Is setter based construction a good idea that is worth the compromise?

Do you have ideas on improving the syntax further?

Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.