Sonntag, 21. April 2013

Active Annotation: Builder Pattern

The @Data annotation shipped with Xtend is a nice demonstration of the concept of active annotations. It generates a constructor, getters for all fields as well as equals(), hashCode() and toString() methods. Although it says that it turns your class into an "immutable value object", this is not really true. For instance, the generated class is not final and thus can have mutable subclasses. Also, it renames fields so that reflection bases frameworks will not find the corresponding field for a given property. Using the big constructor with all fields can be hard to read, especially if multiple fields have the same type.

So I wanted something a little different. For bigger objects you just need a builder. This is what my @Immutable annotation does:
  • getters for all fields
  • a constructor with all fields
  • a builder class with setters for each field of the class
  • a static builder() method for fluent building in Java clients
  • a static build(Procedure) method for closure-style building in Xtend clients
  • equals(), hashCode() and toString() implementations
  • makes the class final
  • forbids inheriting from other classes
Here you can see the annotation and the build(Procedure)-method in action.

In the future I plan to analyze the classes' fields and generate defensive copying of known mutable classes. For instance, if the class contains a Date field, it will automatically be copied in the constructor and the getter. A warning will be issued for all cases where the annotation does not know whether a field is of a mutable type. This warning will be suppressed if the user specifies his own implementation of a public getter and a private setter (used by the constructor).

Also, there is a lot of fine tuning to do like not overwriting the toString() method if the user has already specified a custom one.

So for everyone who's interested, here is the full implementation of the annotation processor. It's just over a hundred lines of code that saves me thousands of lines of boilerplate in my data classes.