Value Types in Java
Before we can talk about how great AutoValue is, let’s look at the problem it solves: value types.
A value type is simply an immutable objects whose equality is based on property values, as opposed to identity. Think of a
1 2 3 4
This is a simple value type with two properties,
currency. If we have two money objects with the same currency and amount, they are considered equal, regardless of object references.
While this seems perfectly straightforward, Java doesn’t make it quite this simple. Value types should be immutable, but our current Money object doesn’t enforce that. So we need to update it like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
As you can see, we have to make the class final so that it can’t be subclassed (has to do with equality guarantees), make our fields private, use getters to retrieve the values, and add a constructor so that users can create Money objects. This turned our simple little 4 line class into 14 lines.
But we’re not done yet. Equality is still by reference. This means that
$2 != $2. We need to implement
equals(), and also
hashCode() if we ever want to use this object in a set, or as keys in a map.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
equals() I’ve simply asked IntelliJ to create them for me, which is great as long as I don’t change fields. This has also turned my not-so-little 14 line class into a full grown 29 line class.
But what happens if we want to log this object? Surely we’d like something a little better than
Money@12CE469 to show up in our logs. Therefore we need to add a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
And now we’ve gone from 4 lines to 34 lines in this class. If you believe in the relationship between lines of code and potential for bugs, then you see the problem here, to say nothing of the extra work required to create and manage this thing.
AutoValue to the Rescue
This is where AutoValue comes in. It’s an annotation processor that generates all of the mundane value-type code for you, so you can focus on more important things.
To add AutoValue to your project, you simply need to add it’s single dependency to your annotation processing classpath. Being in the annotation processing classpath means that no dependencies of AutoValue get added to your final artifact, only the generated code.
1 2 3
To use AutoValue to turn our Money object into a full fledged value type, you simply need to add an annotation to the now abstract class.
1 2 3 4
Behind the scenes, AutoValue generates all of the private fields, the constructor,
toString() for you. This generated class, named by simply prefixing your class name with
AutoValue_, is package private, so all you or your users ever really deal with is the annotated Money class.
Since the subclass’s constructor is package private, you also need to add static factory methods, Item 1 in Josh Bloch’s Effective Java, to create your objects.
1 2 3 4 5 6 7
With that we’re done. This value class is effectively identical to the previous example, but is contained in 7 lines of code, and only contains what we need.
One feature that shows the benefit of AutoValue’s method of subclassing is that you can add any additional code to the class you want. So, for instance, if you have derived fields, they can be added to the Money class without the need for helper classes.
1 2 3 4 5 6 7 8 9 10
Testing: The Hidden Benefit
One benefit of generated code is that it doesn’t need to be tested. Whereas in the earlier example all of the code would need to be tested to guard against regressions and ensure proper functionality, with AutoValue, since the generator itself has been tested and is known to produce correct code, we don’t have to worry about testing all of the boilerplate code.
So now that you can easily create value types in Java, what if you want to use your generated value types with other systems, like JSON serializers, or Android’s Parcel class? That’s where extensions come in.
With the release of AutoValue 1.2-rc1, we finally have support for AutoValue Extensions. With Extensions you can have additional functionality, in most cases simply by adding a dependency to your annotation processor classpath.
For example, say you wanted your Money object above to be Parcelable. By simply adding the AutoValue: Parcel Extension to your annotation processing classpath and making your class implement Parcelable, the generated code will be Parcelable.
1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
This alleviates you from having to write any of the boilerplate Parcelable code yourself by generating optimized, tested code for you.
There are several AutoValue extensions already available to help you generate clean code.
Here are just a few examples:
- AutoValue: Gson Extension
- AutoValue: Moshi Extension
- AutoValue: Cursor Extension
- AutoValue: With Extension
- AutoValue: Redacted Extension
If you don’t find one that fits your needs, any of these extensions would make a great starting point to create your own.
In a future blog post I’ll go into detail with the Extension API, looking at how you can create your own AutoValue Extensions, and also dive into these extensions in more detail.