Java Meets “Auto” Immutable with AutoValue

“Hopefully nobody still writes Beans” — Jake Wharton

Back in the day, when I was first working on Android apps, I was told to write data model classes as “Beans” — a class that has getters and setters for every property in the file. At the time, that was the standard, but that was a few years ago.

Eventually, Android developers started to realize that Beans wasn’t so great for some cases. Some of the disadvantages with Beans are mutability and boilerplates. The mutability violates thread safety and other threads can overwrite the Beans properties. Also, having getters and setters for each property generates an immense quantity of boilerplates as a class grows.

But don’t worry! You shouldn’t be embarrassed or feel like a bad dev if you’re still writing Beans. I only stopped writing Beans recently! And hopefully, after reading this post, you can find alternates to writing Beans too because I am here to show you a better way to create and manage the data model class.

What is Beans?

Asteroid model with “Beans” style

Here is an example of Beans class. This Asteroid class has three properties and each property has getter and setter to reference and change its value. As you can see, everything is basically mutable here since there’s a mutator in every single property. Therefore, you can change any of these instance properties from any class, any thread.

You may want a mutable object for some cases such as a class, Comment. Comment allows user to post, update, and delete. Let’s say the Commentclass has message and updatedAt properties and both of them need to be able to initialize, update, and delete.

How about these examples?

  • You have an app that gets data from an API server and displays it on Android screen.
  • A user enters some data on a screen and that data is sent to an API server upon click of the “send” button.

In both these cases, a model can be immutable. In the first example, when you get data from the API server, you initialize a data model class and you will only reference its properties in any view classes to display it. In the second example, once the user enters the data and clicks the “send” button, you initialize a data model and pass to an API call handler class, then the handler class can only read properties to send it to the server.

In these cases, you set data once and after that you would only reference the properties. Hence, we don’t need to allow these properties to be mutable — No “Beans”. Please keep these cases in mind, as we’ll be returning to it later on.

Now, let’s use the Asteroid example from above and make the class immutable.

Making It Immutable

Asteroid model with immutable approach

Here, properties are only set once via a constructor and each field has a getter to read a value. This Asteroid class is not mutable. I purposefully omitted equals, hashCode and toString functions in the “Beans” example. However, if you write a model class in full, you would need all of those functions. While equals and hashCode may not be that popular, you should at least write toString so that other developers can see the actual instance content rather than “Asteroid@66x1b21” in a log.

Looking at the class, it’s full of boilerplates and is mainly about name, absoluteMagnitude, and closeApproaches. All the info I need to know is in the first few lines and the rest is just blah blah blah.

You can say that Android Studio generates all getters, equals, hashCode and toString functions when you first create a file. That part is easy. However, the problem becomes more obvious when you try to add a field to an existing class.

To add one field to the class, you would add: (count the fraidy cat emojis for a quick answer)

Adding a single field to an existing model class, as shown using fraidy cat emojis

The green highlighted lines with fraidy cat emojis are added lines. How many of those emojis can you see in this image? There are SIX. Six things you need to write just to add a single property. Android Studio isn’t even a big help here — you basically need to write the boilerplates, no shortcut around it.

Except… what if you use “AutoValue”?

AutoValue- Your New Best Friend

AutoValue is an annotation based library that generates an immutable model class with auto-generated equals, hashCode and toString using simple annotation. It also gives us the flexibility to add custom functions in the class.

Additionally, there are extension packages to work with Parcelable and JSON converters.

Let’s take the above Asteroid class and use AutoValue on it.

Installation with Gradle:

apt ‘com.google.auto.value:auto-value:1.2’
provided ‘com.jakewharton.auto.value:auto-value-annotations:1.2-update1’

Modify Asteroid class:

Asteroid model with AutoValue

First, add AutoValue annotation on top of the class name. Then change each field to a “public abstract” function; an alternate to a getter. Since we changed it to abstract, the next step is to alter the class to be abstract as well. Then we create a static factory method to instantiate AutoValue’s Asteroid object. Finally, remove all of the boilerplates you had in place before. ALL-OF-THEM.

Without and With AutoValue

Now, you’ve probably noticed how small the class became. We got rid of allequals, hashCode, toString functions and the getters (well, sort of). Now we have more or less ONLY information we want to see — one factory method (constructor) and some abstracted getters.

Once you compile this code, AutoValue automatically generates a class called $AutoValue_Asteroid which implements all the getters, equals, hashCode and toString.

Okay. This is cool, then… how can I apply AutoValue to the case I mentioned earlier in this post?

You use an app that gets data from an API server and displays it on Android screen.

AutoValue-Moshi + Retrofit2

I picked the Moshi converter for this but you can choose another converter such as Gson, Jackson, etc.

Install with Gradle:

apt ‘com.ryanharter.auto.value:auto-value-moshi:0.3.0’

Modify Asteroid class:

AutoValue with Moshi

To make the AutoValue class work with the Moshi converter, add Json annotation on top of an abstracted getter line if needed. Then add this block:

public static JsonAdapter<Asteroid> jsonAdapter(Moshi moshi) {
return new AutoValue_Asteroid.MoshiJsonAdapter(moshi);
}

If a class is used for a JSON response, you can remove a factory function, which is taken care by the converter.

To apply this with the Retrofit2, the HTTP client, you want to create the Retrofit builder like this:

Apply AutoValue Moshi to Retrofit2

The rest of the code is the same as how you’d normally write with Retrofit2 and Android framework.

To Sum Up

AutoValue introduced two major benefits; it automatically creates an immutable object and clears away most of boilerplates in data model classes. It also works great with Parcelable and JSON converter. There could be some cases where you want a mutable object. However, if that is not a case, why not make it immutable with AutoValue!

For Further Reading

*I recommend this post by Ryan Harter:
http://ryanharter.com/blog/2016/05/16/autovalue-extensions/

*A few months ago, I came across a presentation video by Jake Wharton. This video is one of my favorite Android videos in 2016 and I would definitely recommend watching it.


Originally published at developer.capitalone.com.

For more on APIs, open source, community events, and developer culture at Capital One, visit DevExchange, our one-stop developer portal. https://developer.capitalone.com/

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.

Responses
The author has chosen not to show responses on this story. You can still respond by clicking the response bubble.