Immutable Value Objects in Java with Google AutoValue

Java 8 addressed many of the biggest pain points in the Java language, from lambda expressions and functional-style collection operations to the new Joda-like date and time API.

One notable miss is something like Scala’s case classes or Kotlin’s data classes: a simple way to define immutable value objects. The amount of boilerplate for a class holding just a couple of fields is ridiculous: you need not just a constructor, member variables and setters but most likely also equals, hashCode, and toString implementations, resulting in 50 lines of code in Java versus 1 line in Scala.

Attempts to make things easier range from IDE code generation to the Project Lombok hack. Thankfully, 20 days ago Google released version 1.0 of AutoValue, arguably the best solution to date.

This presentation by the project authors gives an excellent background on the alternative approaches and why they decided to create AutoValue instead. In this post I’ll just show a simple usage example and then see if this approach works well with JSON serialization.

Example

Here’s what a trivial Person class looks like with AutoValue:

@AutoValue
public abstract class Person {

    public static Person newPerson(Long id, String name, List<String> aliases) {
        return new AutoValue_Person(id, name, aliases);
    }

    public abstract Long getId();
    public abstract String getName();
    public abstract List<String> getAliases();

}

AutoValue runs as as an annotation processor, detects the @AutoValue annotation and automatically generates an AutoValue_Person implementation class – that’s what we return in our factory method. Nothing AutoValue-specific is exposed in our API and it doesn’t even require any runtime dependencies!

The only small annoyance is getting your IDE to find the auto-generated implementations and stop complaining about undefined classes. This differs depending on your IDE and build tool – see this blog post for more – but it’s certainly possible being a standard annotation processor. Building with Maven or Gradle requires no additional configuration.

JSON Serialization

One common use of value classes is as transfer objects serialized to/from JSON, so let’s see how easy it is to use AutoValue with what’s probably the most popular JSON library for Java: Jackson.

Assuming we added the jackson-databind library as a dependency, serializing Person objects to JSON works out of the box, since Jackson can read all the values from the getter methods:

ObjectMapper objectMapper = new ObjectMapper();
Person person = newPerson(1L, "Joseph Bloggs", asList("Joe"));
System.out.println(objectMapper.writeValueAsString(person));
// -> {"id":1,"name":"Joseph Bloggs","aliases":["Joe"]}

However, trying to deserialize that JSON string back into a Person object won’t work, because there are not setters. This is not specific to AutoValue, it applies to any immutable object. All we need to do is add some annotations to our factory method so that Jackson knows how to create our immutable objects:

@JsonCreator
public static Person newPerson(@JsonProperty("id") Long id,
                               @JsonProperty("name") String name,
                               @JsonProperty("aliases") List<String> aliases) {
    return new AutoValue_Person(id, name, aliases);
}

so deserializing now works as well:

ObjectMapper objectMapper = new ObjectMapper();
String json = "{\"id\":1,\"name\":\"Joseph Bloggs\",\"aliases\":[\"Joe\"]}";
Person person = objectMapper.readValue(json, Person.class);
System.out.println(person);
// -> Person{id=1, name=Joseph Bloggs, aliases=[Joe]}

As you can see, working with AutoValue-generated objects really is no different than working with hand-written immutable objects. Except it saves you a lot of time and potential errors by not having to write boilerplate code.

4 thoughts on “Immutable Value Objects in Java with Google AutoValue

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s