Image Blog Understanding Autovalue and Extensions
September 15, 2017

Understanding AutoValue in Java

Java Tools

AutoValue can be a valuable tool for developers to make their code cleaner and less verbose, but it can be difficult to use properly.

In this article, we will look at what AutoValue is and how it can help you keep you streamline your code. We'll also explore how the AutoValue extension mechanism and the AutoValue Moshi extension works.

What Is AutoValue in Java?

AutoValue is an annotation processor from Google for generating immutable Java classes in Java. AutoValue was created to reduce the amount of code needed —leaving less room for error. Because of this, it a great tool for developers wanting clean and concise code.

With AutoValue, a developer only needs to write a minimalistic abstract version of a data class:

import com.google.auto.value.AutoValue;

@AutoValue
abstract class Animal {
  static Animal create(String name, int numberOfLegs) {
    // See "How do I...?" below for nested classes.
    return new AutoValue_Animal(name, numberOfLegs);
  }

  abstract String name();
  abstract int numberOfLegs();
}


The actual implementation is a subclass that is generated at compilation time. It includes the fields corresponding to the abstract methods, a constructor, hashCode(), equals() and toString() implementations. The implementation class is generated at compile time, so no Reflection API is used at runtime. Even after obfuscating, the code toString() still includes the original field names in the output.

By default, all fields are not-nullable. The constructor throws a NullPointerException if any of the values are null. To mark a field as nullable, you can use any annotation with the name Nullable.

AutoValue Builders

To create an instance of the value class, you can use the static create() method. Still, sometimes invoking the create() method with multiple arguments gets inconvenient. In these cases, a fluent Builder class becomes handy. AutoValue also generates them automatically:

import com.google.auto.value.AutoValue;

@AutoValue
abstract class Animal {
  abstract String name();
  abstract int numberOfLegs();

  static Builder builder() {
    return new AutoValue_Animal.Builder();
  }

  @AutoValue.Builder
  abstract static class Builder {
    abstract Builder setName(String value);
    abstract Builder setNumberOfLegs(int value);
    abstract Animal build();
  }
}


The build() method also checks for nulls and can throw an IllegalStateException with a list of fields that have no value. This list also includes the primitive fields for which the set methods were not called.

AutoValue IDE support

IntelliJ IDEA and Android Studio users also have an AutoValue plugin that generates or updates the create() method or the Builder class automatically.

My experience with IntelliJ (and Android Studio) regarding annotation processors is pretty bad:

  • When adding a new @AutoValue class, I need to manually compile the classes so it would generate the sub classes.
  • When renaming an @AutoValue class, I need to clean the project so it wouldn't complain about the old class which does not compile anymore (this is solved by using JRebel for Android with its incremental compiler).

I hope the development experience gets improved in the future IDE versions.

How are You Compensating for the Developer Shortage?

Java developers are in shorter supply these days, impacting everything from time-to-market to the user experience. Our experts dissected trends that are impacting labor and explored the downstream effects they're having on business outcomes in this on-demand webinar. Learn how to help your developers be more productive and efficient to help offset the pain of a thinly-stretched team.

▶ Solving the Java Developer Shortage

AutoValue Extensions

AutoValue has a nice way of extending the behavior of the generated classes: AutoValue extensions. Ryan Harter has provided a great overview of that, including a list of available extensions. He is also the author of multiple extensions: auto-value-parcel, auto-value-gson and auto-value-moshi.

The main idea behind the extensions is to supply the generated classes with the integrations with the libraries and frameworks you might want to use with the data classes, like serialization libraries for example.

In short, the extensions work by generating a subclass of the generated AutoValue class. When several extensions need to work on a single class, they generate a chain of subclasses, implementing the interface you marked with @AutoValue.

Here is an illustration of that, taken from Ryan's article I mentioned above:

Ryan Harter illustration of a chain of subclasses generated by autovalue extensions

You use the most specific implementation and get all the functionality added by each extension through the Java inheritance mechanism.

The extension API is good, but at the moment it lacks the support for Builder classes. This is required for correctly handling custom default values.

AutoValue Moshi Extension

Before we talk about the auto-value-moshi extension, it's good to know what it is supposed to do. Moshi is a modern JSON library from Square (for both Android and Java platforms). It uses the same streaming and binding mechanisms as Gson, but it is faster and simpler, as well as more robust.

The core class in Moshi for mapping a Java object to JSON and vice versa is JsonAdapter:

public abstract class JsonAdapter {
  public abstract T fromJson(JsonReader reader)
  public abstract void toJson(JsonWriter writer, T value);
  ...
  public interface Factory {
    /**
     * Attempts to create an adapter for {@code type} annotated with {@code annotations}. This
     * returns the adapter if one was created, or null if this factory isn't capable of creating
     * such an adapter.
     *
     *

Implementations may use to {@link Moshi#adapter} to compose adapters of other types, or * {@link Moshi#nextAdapter} to delegate to the underlying adapter of the same type. */ JsonAdapter create(Type type, Set annotations, Moshi moshi); } }


The create() methods accept a Type instead of a Class to also support generics. This is a must for collections as the adapters are selected only by types, not the actual runtime values. A set of annotations are passed to add qualifiers to the types, for example we could create @IgnoreCase for enums to parse them, ignoring their case. The Moshi object here is for looking up other adapters. It's required for any composite types: collections and Java classes to find the corresponding adapters for their members.

Moshi works with POJOs out of the box via the Reflection API. So it might be a bit slower in terms of performance. Also, the field names cannot be obfuscated. Developers could write custom adapters for their Java types to fix this, but it's a boilerplate and a waste of time. That's what the AutoValue Moshi extension does for us. It generates an adapter for each AutoValue class that uses plain Java without the reflection (with some exceptions). We only need to add one static method for each @AutoValue class:

@AutoValue public abstract class Foo {
  abstract String bar();
  @Json(name="Baz") abstract String baz();

  public static JsonAdapter jsonAdapter(Moshi moshi) {
    return new AutoValue_Foo.MoshiJsonAdapter(moshi);
  }
}


We could add those adapters to a Moshi object one by one, but it's easy to forget some. There is a more convenient option to let it also generate a factory for us that works for any number of AutoValue classes we have in the current module:

@MoshiAdapterFactory
public abstract class MyAdapterFactory implements JsonAdapter.Factory {
  // Static factory method to access the package 
  // private generated implementation
  public static JsonAdapter.Factory create() {
    return new AutoValueMoshi_MyAdapterFactory();
  }
}


So the extension actually has two compile time processors:

Both see the class structures via javax.lang.model APIs and write the generated source code using JavaPoet, another library from Square for writing Java source files. It's a successor to a former JavaWriter library. If you feel like you need more information about the annotation processors, here is a wonderful article about annotation processors written by Hannes Dorfmann.

Design Draft for Converting the .properties Files

Now let's take a look at what one needs to do to create an extension for AutoValue. Remember, initially I wanted a way to instantiate AutoValue classes based on the property files.

Every AutoValue class would need to have an adapter to read and write property files:

interface PropsAdapter {
  void write(PropsWriter out, T value);
  T read(PropsReader in);
}

interface PropsWriter {
   void setProperty(String key, Class type, T value);
}

interface PropsReader {
   T getProperty(String key, Class type);
}


It sounds quite easy to implement. Just go over the AutoValue classes, pick the ones that have a static method for the PropsAdapter declared and generate the code for serializing the instances, field by field, into a properties file.

public static PropsAdapter propsAdapter() {
  return new AutoValue_Foo.PropsAdapter();
}  


In fact, some problems exist which we perhaps explore in the second part of this blog post, when the extension code is publicly available to talk about.

The first problem you will encounter is the type safety: you want the code to be type-safe about the properties and handle generic collections well. Collections are hard anyway because properties files don't have the built-in syntax for the collections. Meaning that not only do you need to agree on that, the extension should also somehow make it configurable.

Then you'll encounter another complication with Java primitives. AutoValue's approach is that the primitive values are always required. However, during deserialization, Moshi and Gson do not require them. They simply skip setting those fields, leaving them with the default values like 0 or false when they are not found in the JSON input. Since AutoValue doesn't let you skip setting any required fields, the corresponding extensions explicitly set the fields as 0 or false in that case. This is somewhat in violation of AutoValue's own logic.

Not requiring additional ProGuard rules would be amazing. I mentioned that AutoValue generates code at compile time, so no Reflection API is used at runtime. auto-value-moshi actually breaks this rule when annotating a property with an annotation that is a @JsonQualifier. It looks up those annotations at runtime by an original method name, stored as a String so ProGuard doesn't replace that. In that case, users still need custom obfuscation rules.

All in all, it's not an easy problem. In the next blog post I will try to concentrate on how I handled these questions and with which design choices.

Final Thoughts

I wanted to create an AutoValue extension for converting .properties files based on the AutoValue Moshi extension.

During this exercise I learned a bit about AutoValue, its extensions, generating Java source code with JavaPoet, and pondered about issues with serialization.

In this post, we looked at the base knowledge required to understand and implement an AutoValue extension. Soon enough, we can hopefully also look at the source code for the AutoValue properties extension!

 

Survey Says: Redeploy Times are Hurting Development

Long Microservice redeploy times still plague teams of all sizes. In a Perforce survey, 26% of developers report that it takes a whopping ten minutes or more to remotely deploy their containerized environment. That means less time for new features, improving performance, test coverage and more. 

When you use JRebel, you can skip redeploys. Give it a test drive with a free 14-day trial, and see how much you can save by first calculating your ROI.

TRY JREBEL FOR FREE   FIND MY ROI