Versão em português aqui.
AutoValue still a good solution for Java only projects. After almost 3 years, although, I do recommend avoid AutoValue and jumping right to Kotlin Data Classes. Keep in mind that this is a 2016 post.
Less is more. That’s one of the phrases repeated more often in every field. For the software development field, less code written can mean more stability, more simplicity and more agility. In this article, I will introduce a general vision of an excellent set of tools for software development: AutoValue and it’s extensions.
AutoValue in general
For this example, we will create a class with data for a person. The class PersonData contains some basic data for an application. It stores the data in final attributes, that can be accessed using getters. It also overrides the methods toString(), hashCode() and equals(Object). That’s a very common pattern in Android projects (and Java projects in general), known as value class. We can see in the code sample below that this class already have more than 70 lines.
This article contains several code samples, presented as Gists. To reduce the number of lines, some of those are in a very simplified formatting. The complete sample project can be found at GitHub.
A lot of code to maintain, analyze and review.
Now, to take complexity even further, let’s assume that this class will host a new attribute. To do that, we must take several steps: Declare the attribute, include a parameter in the constructor, create the getter method, add the attribute to equals() method, add the attribute to hashCode() method and add the attribute to toString() method. If the developer make any mistake during one of these steps, the code will fail in some important moment. And all that work just to make a simple class. If this class is used in some sort of serialization as JSON or implment an interface like Parcelable, much more code would have to be written, maintained and reviewed.
The purpose of AutoValue and it’s extensions is to reduce the amount of code that must be written to create simple classes. The developer then must only declare the class and all the heavy work is done by code generators. To achieve that, we must setup some tools in build scripts and apply some annotations in our classes.
For starting, make sure you are using Android Build Tools 2.2 or superior. Otherwise, you will have to enable the android-apt plugin and replace the instruction annotationProcessor from apt.
Previous versions from this post stated that the android-apt plugin was mandatory. Since the release of Android Build Tools 2.2, that is not true anymore.
In the following step, we include the AutoValue dependencies in our module. The depencency of annotationProcessor type indicates that the AutoValue will be applied when the code generation plugin is runned. The provided indicates that AutoValue doesn’t need to be included with the project, since it is used only in build time.
Now we can refactor our class to a much simpler form. The annotation @AutoValue is added, indicating that it must be analyzed by the tools. We also must make the class abstract, since it’s implementation will be generated and not created manually. The attributes are removed and corresponding abstract methods are defined. Each method definition indicates the format of the associated attribute, the modifiers and annotations defined for it. Our class now has a bit more than ten lines.
Since we now have an abstract class, we must define a way to create new instances of it. By convention, AutoValue classes allow new instances by publishing a static method create. This method instantiates a new object and returns it in a transparent way.
Note that we are calling the constructor of the class AutoValue_PersonData. This is the class generated by AutoValue with all the concrete implementation of all that we defined early: the attributes and methods are on it.
The first time we include this call in the create method, the code will present an error indicating that AutoValue_PersonData does not exists yet. Remember that AutoValue classes are generated in build time, so you must rebuild your project every time we change something in the definitions. We have a snippet of the generated class in the sample below.
We can notice that the concrete class inherits from our abstract, as we expected. It includes a constructor with all the fields and the initialization of they. The concrete class also has the same package as the abstract, simplifying organization and management of code.
In some situations, it may not be interesting to use a constructor with several parameters. In our sample, we have some String parameters that are very error prone for the callers. Thinking about this problem, the AutoValue allows the definition of Builders. That means we can generate classes that allow the initialization of objects using the Builder Pattern. This way, we can create new instances by calling a sequence of methods with the name of the attributes. That prevents misleading parameters’ order and other problems. To create a Builder, we must define an intern abstract class, as the sample below.
All the attributes have their equivalent method. The create method is also replaced by a new static method, as the sample.
Now the clients of our class are able to create new instances easily in a very fluid way.
Looking to the generated class again, we can notice that there are some runtime checks in it. By default, AutoValue classes don’t allow null fields, throwing runtime exceptions to warn the developer that something may be wrong.
If your class must accept null values, the attribute must be annotated with @Nullable. This way, the consistencies won’t be generated and null values will be accepted.
Our class is now much more simpler than in it’s first version. We can declare new attributes very easily or adjust the existing ones. We also have useful features as a builder. But the utility of this class in a real life project is still limited, once the generated class is still very simple.
The recent versions of AutoValue added a very useful feature called AutoValue Extensions. Using these extensions, it is possible do include custom code generators to our projects. New intermediary classes can be generated between the absctract and the concrete one, allowing much more features and use cases.
For starting, let’s set up the extension to allow Parcelable classes to be generated. To do that, a new dependency is added to the build.gradle of our module.
Now we just have to make our PersonData class implements Parcelable. All the methods for this interface will be generated by the extension, the developer doesn’t need to write any more code.
Our class AutoValue_PersonData now contains all the implementation of Parcelable. That was easy, wasn’t it?
The GSON integration extension demands a bit more work, but worth the effort. Again, we do start adding dependencies to our build.gradle.
After that, we must define the classes that must be integrated with GSON. To do that, a typeAdapter method is defined in the class:
Again, an error will be displayed, since our generated classes are outdated. Building the project makes the error disappear. This process will generate a new class in our project: the class AutoValueGsonTypeAdapterFactory makes the connection between our generated classes and the GSON engine. For that reason, we must register this class in the GSON instances created in our project.
This kind of integration is very useful for communication between applications and backends and can also be used with Retrofit. Using the extension makes much more easier to define classes used in the communication, since all the work the developer has is to define the abstract classes and add the adapter factory method.
There is much more
The two extensions presented so far are very useful for Android the projects. But there are much more extensions for diferent situations, such as Moshi Extension, Cursor Extension, With Extension and Redact Extension.
Using AutoValue and Extensions in Android projects makes some steps of the application development much faster and allow the developer to pay attention to much more important steps. The simple and repetitive tasks are now automatized, creating a much more efficient environment.
Did you like AutoValue? Do you have a different use case? Don’t forget to comment here or to ping me at Twitter.