logo

What are value type objects in Java?
Before we begin to explore AutoValue, we must know about value types in Java and the significance of value-typed objects.
A value type is simply an immutable object whose equality is based on property values rather than identity. For instance, consider a Money object –

 
public class Money {
  public Currency currency;
  public long amount;
}

As observed, the value type Money has two properties – amount and currency. Accordingly, any two Money objects having the same currency and amount as properties will be considered equal. However, things are not so straightforward in Java. By definition value types should be immutable, however the above code doesn’t follow the definition. Going by definition, we need to correct the code as mentioned below –

public final class Money {

  private Currency currency;
  private long amount;

  public Money(Currency currency, long amount) {
    this.currency = currency;
    this.amount = amount;
  }

  public Currency currency() {
    return currency;
  }

  public long amount() {
    return amount;
  }
}

In other words, we need to make the class final so that it can’t be sub-classed; make our fields private, use getters to retrieve the value and add a constructor so that users can create a Money object without error. However, this potentially elongate our class from line 6 onwards.

But hold your horses, the value type is not fully written yet. Here, equality is by reference. To make the Money object usable in a set, we also need to implement equals() and hashCode(), which can be done easily using intelliJ, thus elaborating the class to –

public final class Money {
  private Currency currency;
  private long amount;

  public Money(Currency currency, long amount) {
    this.currency = currency;
    this.amount = amount;
  }

  public Currency currency() {
    return currency;
  }

  public long amount() {
    return amount;
  }

  @Override public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Money money = (Money) o;
    if (Double.compare(money.amount, amount) != 0) return false;
    return currency.equals(money.currency);
  }
  
@Override public int hashCode() {
    int result;
    long temp;
    result = currency.hashCode();
    result = 31 * result + (int) (amount ^ (amount >>> 32));
    return result;
  }
}

The above class works fine as long as we don’t change fields. However, we also need to add a toString() method, if we ever need to log this object.

public final class Money {
  private Currency currency;
  private long amount;

  public Money(Currency currency, long amount) {
    this.currency = currency;
    this.amount = amount;
  }

  public Currency currency() {
    return currency;
  }

  public long amount() {
    return amount;
  }

  @Override public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Money money = (Money) o;
    if (money.amount != amount) return false;
    return currency.equals(money.currency);
  }
 
 @Override public int hashCode() {
    int result;
    long temp;
    result = currency.hashCode();
    result = 31 * result + (int) (amount ^ (amount >>> 32));
    return result;
  }
 
 @Override public String toString() {
    return "Money{" +
        "currency=" + currency +
        ", amount=" + amount +
        '}';
  }
}

As you can see, to make the Money object work flawlessly in all possible use cases, we have gone from line 4 onwards. These complications increase the time to create and manage the code as well as increase the risk of encountering bugs. Imagine creating the different value type objects where you need to implement equals, hashCode and toString in a repetitive, formulaic yet erroneous manner.

Despite the shortcomings, value types are extremely common in Java projects. Writing the value types for the first time can be still managed through IDE templates, but once implemented, they continue to burden the reviewers, editors and users, as well as attract bugs.

What is Autovalue – how does it help?
AutoValue is an annotation processor which provides an easier way to create immutable value-type code. It uses significantly less code, leaving little room for error, and at the same time allows you to code almost every aspect of the class you are creating.
To add AutoValue to your project, you simply need to add its single dependency to your annotation processing classpath. This ensures that no dependencies of AutoValue get added to your final artifact, only the generated code.
For instance, we can turn our Money object into a fully-functional value type through AutoValue by simply adding an annotation to the abstract class –

@AutoValue public abstract class Money {
  public abstract Currency currency();
  public abstract long amount();
}

Thus generated class, created by simply adding the prefix AutoVaue_ to your class name is a package private – that is, it generates all the private fields such as the constructor, hashCode(), equals() and toString() – and the users have to deal with the annotated Money class alone. The only thing you need to add to this annotated class code is static factory methods, since the subclass’s constructor is package private, resulting in –

@AutoValue public abstract class Money {
  public abstract Currency currency();
  public abstract long amount();
  public static Money create(Currency currency, long amount) {
    return new AutoValue_Money(currency, amount);
  }
}

The value class created using AutoValue is thus identically equal to the original code we had written, however it is effectively reduced from 34 lines to 7 lines and contains only the information a user might need.

Apart from reducing the length of code and chances of errors and bugging, AutoValue method also lets you add additional code to any class you want. For example, if you want to add derived fields to the Money class, you can do so without requiring helper classes, viz. –

@AutoValue public abstract class Money {
  public abstract Currency currency();
  public abstract long amount();
  
  public static Money create(Currency currency, long amount) {
    return new AutoValue_Money(currency, amount);
  }

  public String displayString() {
    return currency().symbol() + amount();
  }
}

Moreover, an AutoValue generated code doesn’t need testing. All the earlier examples of Value type codes needed to be tested to ensure functionality and spot errors and fix loopholes. But since the AutoValue generator is tested and known to produce correct code, we can save significant time by forgoing testing of the boilerplate code.

Understanding Compile Time Annotation Processing in AutoValue
As stated earlier, AutoValue is a compile time annotation processor – which means that it runs only when you compile the code, and not when your mobile app is running. In simpler terms, AutoValue doesn’t significantly affect your mobile application’s size of performance since it adds the generated code to final binary.

Nevertheless, if you are not careful, you might add the AutoValue and its dependencies in your final binary. In this case, it might seriously impact the efficacy and performance of your app (especially in Android devices). To avoid incurring such performance issues, you should add AutoValue to the right configuration, in such a manner so as to include it only in the annotation processing phase of your build process.

To add AutoValue without errors in an Android app, it’s advised to first install the android-apt gradle plugin and add AutoValue to the apt configuration to get proper code implementation.

dependencies {
  provided 'com.google.auto.value:auto-value:1.2'
  apt 'com.google.auto.value:auto-value:1.2'
}

In order to use AutoValue with Gradle in a Java project, you can add dependencies to the compileOnly and apt configurations after installing the apt plugin.

dependencies {
  compileOnly 'com.google.auto.value:auto-value:1.2'
  apt         'com.google.auto.value:auto-value:1.2'
}

Maven users can simply add the dependency to the provided scope

<dependency>
  <groupId>com.google.auto.value</groupId>
  <artifactId>auto-value</artifactId>
  <version>1.2</version>
  <scope>provided</scope>
</dependency>

We need to list dependencies in multiple configurations because AutoValue includes both the annotation and annotation processor in the same artifact.

Scanning Classes
Once we have added AutoValue as a dependency, it will run during the compilation phase of app to process all classes annotated with @AutoValue. For each @AutoValue annotated class, it looks for all abstract methods to determine what it should generate. The methods which are inherited by interfaces, take no argument and return a value are considered properties and AutoValue generates appropriate fields and include them in the equals, hashCode and toString methods.

@AutoValue public abstract class Foo {

  // This method takes no arguments and returns
  // String, so it's a property
  public abstract String foo();

  // This method is not abstract, so it's not
  // a property.
  public boolean hasValues() {
    return foo() != null && !foo().isEmpty();
  }

  // This method takes a parameter and returns void,
  // so it's not a property.  Hopefully an extension
  // generates the implementation.
  public abstract void writeValues(OutputStream out);
}

Since AutoValue can differentiate between properties and customized methods, you can use different methods to return generated values as they are, or perform other operations on your object.
Moreover, property methods need not be a part of your public API since they can be package private or protected under the AutoValue subclass.


@AutoValue public abstract class User {

  // Package private property won't be
  // visible outside of the package, but
  // will still be generated.
  abstract String internalFirstName();

  // Publicly available method that uses the
  // internally generated one.
  public String firstName() {
    String name = internalFirstName().trim();
    return Character.toUpperCase(name.charAt(0)) + name.substring(1);
  }
}

In a similar fashion, you can implement your own version of equals, hashCode and toString in your annotated class. These are subsequently inherited by AutoValue and used in the final class.

@AutoValue public abstract class User {
  public abstract String firstName();
  public abstract String lastName();
  public abstract int age();

  // I'm the same person as me from 10 years ago...or am I?
  @Override public boolean equals(Object other) {
    if (!(other instanceof User)) return false;
    User o = (User) other;
    return firstName().equals(o.firstName())
        && lastName().equals(o.lastName());
  }
}

As illustrated, understanding the way AutoValue defines properties adds flexibility to your classes.

The Generated Code
Now that we know how AutoValue creates a leeway for flexibility of properties, let’s decode the components of generated class – such as constructor, member variables, and equals, hashCode and toString.
The Class –

package com.example.autovalue;

final class AutoValue_User extends User {

  ...

}

As you can see, the generated class is package private and final – that is, the AutoValue generated class isn’t visible outside your package and is used only for your annotated base class. Moreover, since it subclasses the annotated abstract class, you can pass around User objects without letting the users know that you use AutoValue to generate the implementation. However, it should be noticed that the final generated class will always be the annotated class name with the prefix @AutoValue_, making it easier for you to access generated code within your annotated class.

The Constructor –

final class AutoValue_User extends User {

  AutoValue_User(
      String firstName,
      String lastName,
      int age) {
    if (firstName == null) {
      throw new NullPointerException("Null firstName");
    }
    this.firstName = firstName;
    if (lastName == null) {
      throw new NullPointerException("Null lastName");
    }
    this.lastName = lastName;
    this.age = age;
  }
}

The generated constructor, which takes into account all the properties in the order they were defined in the annotated class, is also a package private like the generated class. Since all properties are considered non-nullable by default, it checks all the arguments for defined properties.

The Member Variables –

final class AutoValue_User extends User {

  private final String firstName;
  private final String lastName;
  private final int age;

  @Override
  public String firstName() {
    return firstName;
  }

  @Override
  public String lastName() {
    return lastName;
  }

  @Override
  public int age() {
    return age;
  }

}

The member variable fields are considered final and thus, enforce immutability of the type.

equals(), hashCode() and toString()


final class AutoValue_User extends User {

  @Override
  public String toString() {
    return "User{"
        + "firstName=" + firstName + ", "
        + "lastName=" + lastName + ", "
        + "age=" + age
        + "}";
  }

  @Override
  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }
    if (o instanceof User) {
      User that = (User) o;
      return (this.firstName.equals(that.firstName()))
           && (this.lastName.equals(that.lastName()))
           && (this.age == that.age());
    }
    return false;
  }

  @Override
  public int hashCode() {
    int h = 1;
    h *= 1000003;
    h ^= this.firstName.hashCode();
    h *= 1000003;
    h ^= this.lastName.hashCode();
    h *= 1000003;
    h ^= this.age;
    return h;
  }

}

equals(), hashCode() and toString() are also considered as default implementations

AutoValue Extensions
Now that we know how AutoValue works in Java, let’s explore the possibility of using generated value types with other systems such as Android’s Parcel class or JSON serializers.

As we know now, AutoValue is a compile time code generator and does a great job of creating value types. However, these value types are rarely used in isolation. Most mobile apps interact with multiple libraries and services using theses value types, such as JSON or Protocol Buffers, databases, Android Parcelable interface, and more. AutoValue, by itself, has no support for these libraries and services and there’s no fool-proof way to know which services users might turn to.

To support to a vast range of services and add customization to the generated code, AutoValue 1.2 rc-1 introduced AutoValue extensions. These extensions tie into the compile time generation and add functionality to the generated AutoValue value types and generate an optimized, tested code for you.

For instance, if we want to enable our Money object to function in Parcel, we can do so by simply adding the AutoValue: Parcel extension to our annotation processing classpath, making the generated code Parcelable.

dependencies {
  provided 'com.google.auto.value:auto-value:1.2-rc1' // needed for Android Studio
  apt 'com.google.auto.value:auto-value:1.2-rc1'
  apt 'com.rharter.auto.value:auto-value-parcel:0.2.0'
}
@AutoValue public abstract class Money implements Parcelable {
  public abstract Currency currency();
  public abstract long amount();
  public static Money create(Currency currency, long amount) {
    return new AutoValue_Money(currency, amount);
  }
  public String displayString() {
    return currency().symbol() + amount();
  }
}

Extensions add powerful extensibility to AutoValue, and saves time and effort required in writing the boilerplate code. By only generating code you actually need, they also reduce the size of your mobile app.

How AutoValue Extensions Work
AutoValue extensions work in the same way as AutoValue does – by generating a package private implementation of the abstract class – taking cues from abstract class to determine what to generate and then adding functionality to the implementation via subclasses.

Discovery

AutoValue uses Java ServiceLoader API to find available extensions on the class path. Thus, AutoValue extensions can be made available just by adding the required dependency in the build.gradle file. After discovering the available extensions, AutoValue determines the applicable extensions for each class by using the extension’s applicable() method.

Each extension has unique requirements to determine their applicability. As shown above in Android Parcel Extensions, implementing the Parcelable interface to the @AutoValue annotated abstract class determines whether the extension will generate the code for it. Since the annotated class is abstract, you don’t actually have to write any of the implementation, but can simply mark the class with the interface.

Implementation

As we saw, AutoValue extensions function similarly to AutoValue itself in their generation by creating subclasses of @AutoValue annotated class, which contains the implementation. Each extension generates its own subclass starting with the AutoValue implementation, in such a manner so that to allow chaining of multiple, applicable extensions along with the AutoValue implementation.

To do so, AutoValue determines the applicable extensions for each class, prefixes each implemented subclass with $ characters and creates each of them as abstract classes, leaving only the final @AutoValue_ prefixed class as a concrete implementation.

Existing Extensions

Although AutoValue extensions are relatively new, there are several AutoValue Extensions ready to help you generate a clean code. A few of them are listed below for easy reference –

AutoValue: Parcel Extension
By adding implements Parcelable to your value type, the AutoValue Parcel extension generates a fully parcelable implementation of your value type without requiring wrapping or unwrapping to use.

AutoValue: Moshi Extension
It generates type safe JsonAdapter for use with Square’s Moshi JSON serialization library. The generated adapters are optimized to use no reflection, enforce nullability, and ensure type safety.

AutoValue: Gson Extension
Similar to the Moshi extension, AutoValue Gson extension generates optimized TypeAdapter for Google’s Gson JSON serialization library.

AutoValue: Redacted Extension
AutoValue Redacted extension allows you to customize the generated toString() method to redact properties like passwords, credit card details and any other sensitive information.

AutoValue: Cursor Extension
AutoValue Cursor extension generates code to marshal and unmarshal your value types from your database via Android
Cursor objects

AutoValue: With Extension
AutoValue With extension makes it easy to make immutable copies of your value types with slightly different properties. If you need to make a copy of an object, but change the isRead flag from true to false, then it is a great extension to use.

Even if you don’t find an extension you need for your project, any of the available extensions serve as a great starting point to create your own.

Conclusion
“AutoValue is a great tool for eliminating the drudgery of writing mundane value classes in Java. It encapsulates much of the advice in Effective Java Chapter 2, and frees you to concentrate on the more interesting aspects of your program. The resulting program is likely to be shorter, clearer, and freer of bugs. Two thumbs up.”
– Joshua Bloch, author, Effective Java

AutoValue allows developers to write more concise code that focuses on high-level details and delegates the tedious implementation of low-level and error-prone details to AutoValue for automatic code generation.

This, along with the fact that AutoValue is entirely hidden from end users, and doesn’t add any unnecessary overhead to your project, make it a great fit for most mobile apps.

(NOTE: The full implementation of all the examples and code snippets mentioned in the article can be found in the AutoValue GitHub project.)

AUTHOR: Vikas Hiran
No Comments

Leave a Comment

Your email address will not be published.