Friday, August 20, 2010

Every day Jackson usage, part 1: Immutable objects

Here's something that has been requested by multiple readers: an article detailing how to use Jackson to support specific usage patterns. "Jackson usage patterns" if you will.
And first topic I want to cover is.... Immutable Objects.

1. Immutable Objects: what, why?

Immutable objects in Java are objects that are either stateless (behaviour-only) or where state is set at construction and can not be changed.
For example, here is a simple immutable object class:

 public class Point {
  private final int x, y;

  public Point(int x, int y) {
   this.x = x;
   this.y = y;
  }
 
  public int getX() { return x; }
  public int getY() { return y;
 }

The main difference to more common approach is that there are no setters; instead, values of x and y are assigned just once in constructor. That is actually a big difference, and makes this a better approach for many use cases.

But why is it better? One side benefit is just simplicity; fewer methods to write (and maintain), less code, is generally better. But the main benefit is that immutable objects are thread-safe requiring no synchronization: instances can be freely shared and reused between different threads. Immutable objects are also usable as Map keys (in fact Map keys are required to be immutable; or at least never modified while used as a key). This allows very efficient reuse using fly-weight pattern and canonical objects. In fact, unless mutability is specifically needed I try to keep objects immutable, as sort of baseline.

There are also challenges: inability to change state complicates many things. But it is worth noting that immutability only affects instance; and system-level changing of things is quite easy. A common pattern is that of defining factory methods like:

 public class Point {
  // ...
  public Point move(int byX, int byY) { 
    return new Point(x+byX, y+byY);
  }
 }

so that instances are never modified; instead, new instances are constructed based on previous state and delta of some sort. This works nicely for many cases, and is also basis for many lock-free data structures.

But enough about object immutability itself. What is more interesting is if and how one can support such use cases with Jackson.

2. Can Jackson do it?

Initially it might seem unlikely that Jackson can work with immutable objects: after all, aren't Beans required to have getters and setters? "Classic" Java Beans do indeed need setters (mutators). And as a consequence many data binding frameworks (JAXB) require setters, or access to public fields.

But Jackson is neither limited to Beans -- it can work with many other kinds of POJOs -- nor does it require setters for injecting data in. And unlike many serialization frameworks, Jackson does not require access to object fields either (although it can certainly use them when instructed).

As with most Jackson functionality, there are multiple ways to achieve immutability. Let's have a look at them one by one.

3. Simple Immutable Objects using Creator methods

The simplest way to make Jackson produce immutable objects is to instruct it to do what Point class was already doing: make Jackson use constructor to feed in data. This is similar to how Dependency Injection frameworks (like Guice do), and requires use of simple annotations (mix-in annotations work fine):

 public class Point {
  private final int x, y;

  @JsonCreator
  public Point(@JsonProperty("x") int x, @JsonProperty("y") int y) {
   this.x = x;
   this.y = y;
  }
 }

and with this, Jackson will use constructor for passing logical properties "x" and "y", instead of trying to use fields or set methods. The only difference to "regular" properties is that @JsonProperty is not optional for constructor arguments, because bytecode does not store names of parameters and they must be explicitly specified. As with other properties one can configure different aspects, including custom deserializer to use if any, specific subtype to use and so on.

Alternatively one can also define factory methods (constructors and factory methods are collectively referred to as "Creator methods" in Jackson documentation), like so:

  public class Point {
   @JsonFactory
   public static Point makeAPoint(@JsonProperty("x") int x, @JsonProperty("y") int y) {
    return new Point(x, y);
   }
  }

Simple? Note too that you can use Creator methods with other kinds of POJOs. You can also mix and match field access and setter access; if so, Creators are always called first to instantiate an Object, and setters and fields are used afterwards. So you don't have to go all immutable; it is often beneficial to just make certain fields immutable.

4. Faking Immutability

One thing that I did not yet mention is that there is difference between complete immutability (as shown above), and just perceived immutability. By latter I mean that there is no way to change state of the object form outside (except via Reflection, more on this in a minute) -- if you can not change the state, it is as good as immutable. Consider example where fields "x" and "y" were not marked as final: fields are still private, and therefore inaccessible from outside. But they can still be modified via reflection. Since we are not considering security implications here, such bean would be about as good (with respect to benefits of immutability) as one were fields were forced final.

With Jackson you could actually use such fields as well; by having Point class like this:

 public class Point {
  @JsonProperty private int x;
  private int y;

  public Point() { }

  @JsonProperty private void setY(int y) { this.y = y; }

  public int getX() { return x; }
  public int getY() { return y; }
 }

So during deserialization, Jackson would directly access field "x" (using Reflection) and call method "setY" to set value of "y". But since regular code can not modify things further, object would work just as truly immutable one does. This can lead to even simpler code if you just annotate private fields so there is no need to copy values in constructor.

5. Immutabilty by interface / implementation separation

Yet another possibility to achieve perceived immutability is to separate interface of an object into multiple pieces: public read-only (immutable) part that is exposed to application, and mutable part that is used for actual data binding. In our case we could have basic Point interface, implemented by PointImpl class; latter with necessary fields and possibly setters; and interface just having accessors (getters). So as long as application code only deals with interfaces we achieve immutability from design perspective.

But the problem here is that of dependencies; consider a POJO like:

 public class Circle {
  public Point center;
  public int radius;
 }

how would Jackson know that 'center' instance really needs to be of type PointImpl, as Point is just an interface (and can not be created)? Simplest way is to use annotation @JsonDeserialize, which can define actual (sub)type to use (as well as custom deserializer to use; or for Collection and Map types, actual types of values and map keys, and custom deserializers for them too!):

 public class Circle {
  @JsonDeserialize(as=PointImpl.class)
  public Point center;

public int radius; }

and things would work as expected (alternative would be custom deserializer which is bit more work) -- 'center' would always be an instance of PointImpl.

6. Even crazier stuff: Materialize it!

Ok: when I said that one can not just create an instance of an interface, I omitted something cool that Jackson 1.6 will bring: ability to "materialize interfaces". So even though 1.6 has not yet been released (it will be shortly, by end of August!), let's have a sneak peek at this cool feature.

Actually, there is really very little to say, except that instead of having to define PointImpl, one could just register bean materializer (see this piece of documentation for details):

  mapper.getDeserializationConfig().setAbstractTypeResolver(new 
  AbstractTypeMaterializer());

and then Jackson would create an implementation class for any non-concrete Bean (abstract class or interface where all abstract methods are getters or setters) type and use it for deserialization. Pretty cool eh?

This is actually also another fakin' it method: materialized class actually has appropriate access (setters by default), but those are only visible to Jackson itself (or other code that uses Introspection to find methods).

7. More?

These are ways I can think of to make Jackson construct immutable objects. If you can think of others, let me know. :-)

But beyond this, I have some ideas of topics to write about, like

  • How to filter out stuff to serialize (JSON Views, @JsonIgnore)
  • Working with Polymorphic types (@JsonSerialize(as=...), @JsonTypeInfo, default typing, ...)
  • Working with Open Content (models)

And if/when you can think of other Jackson Usage Patterns, please let me know! I'm sure there are more than 3 areas of usage that could use some more documentation.

blog comments powered by Disqus

Sponsored By


Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me at@yahoo.com
Check my profile to learn more.