Cathartic Code Cleanup with @AutoValue

profile pic
profile pic

Sometimes removing code is more satisfying than adding it. For whatever reason, be it an appreciation for clean, elegant software, or a hint of OCD in my personality, I've been known to remove a lot of code in my commits. It's always a great feeling to deprecate that last usage of an old system, refactor duplicated functions, or eliminate some boilerplate code. The latest code cleanup mechanism we've been using on the Pricing team here at Rent the Runway is a tool called @AutoValue (that's a Java annotation, not a Twitter handle), developed and recently open-sourced by Google.

As the name implies, AutoValue helps automate the implementation of value classes; or classes that exist just to encapsulate some fields/values, and don't provide much additional logic. As an example value class, we have an 'Item' class at RTR which contains an ID, a barcode, and a few other fields.

A traditional Java implementation of a class like this can be a hundred lines of code (our Item class is almost that -- 94 lines), after you put in getters, setters, hashCode, equals, and toString functions. Most programmers will auto-generate these with their IDE, but the functions are a burden to review and maintain. The AutoValue documentation's description of these functions is spot on:

"Their wide expanses of boilerplate sharply decrease the signal-to-noise ratio of your code, and constitute probably the single greatest violation of the DRY (Don’t Repeat Yourself) principle we are ever forced to commit."

Why should a class that just holds a few fields take up a hundred lines of code?

That’s where AutoValue comes in. Our 94-line Item class was reduced to 21 lines once we AutoValue'd it. That’s a 78% cut.

Figure 1: Code bloat reduction after 30 minutes usage of @AutoValue ** Actual results may vary




But in all seriousness, if you take a close look at the before and after shots, you’ll notice the new class has exactly the info you want; and nothing else.

Here's the before shot, boilerplate galore:

Figure 2: - Without AutoValue


... and the after shot, simplified with AutoValue:

Figure 3: - With AutoValue


So how does this work?

All you have to do is create an abstract class annotated with @AutoValue, and include a static creator and getters for your fields. Behind the scenes, the AutoValue annotation processor generates derived source code, which you never have to see (but can if you want).

For example, here's the source code that AutoValue generated for our Item class, retrieved from a hidden compiler output directory:

Figure 4: Generated source code [gist][/gist]

Some features, benefits, and uses

1. Prefer AutoValue over tuples. Tuples are often used as a convenient way to quickly represent a collection of fields, but the tuple obfuscates the fields’ meanings and relationships to one another. With AutoValue you can create a much better representation of your data, almost as quickly and succinctly.

2. JSON serialization is easy with Jackson annotations. Just annotate the creator with @JsonCreator and the fields with @JsonProperty, and your class will serialize to JSON.

Figure 5a: Jackson serialization [gist][/gist]

Figure 5b: Serialized JSON [gist][/gist]

3. AutoValue helps prevent null pointers. It includes null checks on every field by default, unless you specify @Nullable fields in the creator. It’s easy to forget null checks and propagate null pointer exceptions (see Figure 2 -- no null checks), so it’s nice that AutoValue takes care of this for you.

4. AutoValue makes your classes immutable. It’s easy to forget to do this too (see Figure 2 again -- no final fields; this is the last time I’ll pick on this code ...), so it’s nice that AutoValue takes care of this as well. (Here are some benefits of immutability)

If you do want mutable fields, AutoValue doesn’t support it directly, but it’s easy to work around. In our Item class, say we want to make ‘barcode’ mutable. Including a 'withBarcode' function kind of cheats (by making a new object), but is a clean way to change a field's value if you don’t mind the overhead of a new instance:

Figure 6: “Mutable” fields


5. … and many more in the AutoValue documentation.


You may need to tweak this slightly depending on your Maven configuration, but we integrated AutoValue into our build using the following pom.xml entries:

- auto-value dependency: [gist][/gist]

- annotation processor plugin [gist][/gist]

- compiler plugin, with annotation processing disabled in the compile phase (compilerArgument -proc:none) to prevent duplicate source files from being generated: [gist][/gist]

That’s all. We hope you have fun reducing your code bloat too!