Why You Should Use Auto Value in Java?

Auto Value generates immutable value classes during Java compilation, including equals(), hashCode(), toString(). It lighten your load from writing these boilerplate source code.

Auto Value is a Java library which helps you to generate value types correctly. A value type is class without identity: two instances are considered interchangeable as long as they have equal field values. Examples: DateTime, Money, Uri… but you also tend to create a great many of these yourself. You know the kind: they’re the ones where you have to implement equals(), hashCode(), and usually toString().

Before Auto Value

Before Auto Value, creating a value type is not easy. In order to implement it correctly, you need to declare all the fields manually, mark them as private, and only expose the getters; you need to implement hashCode() and equals() (often handled by IDE), and keep them up to date when attributes changed; you also need to mark the class as final to prevent subclassing, which guarantees the equality.

A classical value type looks like:

public final class Transaction {

  private long id;

  private String user;

  public Transaction(long id, String user) {
    this.id = id;
    this.user = user;
  }

  public long getId() {
    return id;
  }

  public String getUser() {
    return user;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o)
      return true;
    if (!(o instanceof Transaction))
      return false;
    Transaction that = (Transaction) o;
    return id == that.id && Objects.equals(user, that.user);
  }

  @Override
  public int hashCode() {
    return Objects.hash(id, user);
    }

  @Override
  public String toString() {
    return "Transaction{" + "id=" + id + ", user='" + user + '\'' + '}';
  }
}

Overview

When using Auto Value, everything is easier. You just need to write the following code, and Google Auto Value takes care the rest:

package auto.demo1;

import com.google.auto.value.AutoValue;

@AutoValue
public abstract class Transaction {

  public static Transaction of(long id, String user) {
    return new AutoValue_Transaction(id, user);
  }

  public abstract long id();

  public abstract String user();
}

Behind the scenes, Auto Value generates all the private fields, the constructor, hashCode(), equals(), and toString() for you. The generated class always starts with “AutoValue_“, more explicitly, it naming convention is AutoValue_<MyClass>.

Pretty cool. But what is the real benefit of using Auto Value?

Here’s the explanation from Auto Value: Auto Value is the only solution to the value class problem in Java having all of the following characteristics:

  • API-invisible (callers cannot become dependent on your choice to use it)
  • No runtime dependencies
  • Negligible cost to performance
  • Very few limitations on what your class can do
  • Extralinguistic “magic” kept to an absolute minimum (uses only standard Java platform technologies, in the manner they were intended)

I would also summary it as a comparison table:

Item Without AutoValue AutoValue
Auto attr declaration N Y
Auto getters N N
Auto toString() N Y
Auto hashCode() N Y
Auto equals() N Y
Immutable     Y (*) Y
Auto update toString() N Y
Auto update hashCode() N Y
Auto update equals() N Y

(*) If you implement it correctly.

Maven Dependency

Solution 1. In Maven dependencies, you need to declare 2 dependencies for AutoValue: auto-value-annotations and auto-value. The first one, auto-value-annotations is used for the AutoValue annotations; and the second one, auto-value is used for annotation processing (code generation).

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

The second dependency is declared as provided because the AutoValue processor is only used during compilation, and not used at runtime.

Solution 2. Use annoation processor path from the Maven compiler plugin. In this way the processor is separated from the actual project dependencies.

<dependencies>
  <dependency>
    <groupId>com.google.auto.value</groupId>
    <artifactId>auto-value-annotations</artifactId>
    <version>1.6.2</version>
  </dependency>
</dependencies>
<build>
  <pluginManagement>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.0</version>
        <configuration>
          <annotationProcessorPaths>
            <annotationProcessorPath>
              <groupId>com.google.auto.value</groupId>
              <artifactId>auto-value</artifactId>
              <version>1.6.2</version>
            </annotationProcessorPath>
          </annotationProcessorPaths>
        </configuration>
      </plugin>
    </plugins>
  </pluginManagement>
</build>

More information can be seen here: Stack Overflow: Maven 3 - How to add annotation processor dependency?

Builder Pattern

package auto.demo2;

import com.google.auto.value.AutoValue;

@AutoValue
public abstract class Transaction {

  public static Builder builder() {
    return new AutoValue_Transaction.Builder();
  }

  public abstract long id();

  public abstract String user();

  @AutoValue.Builder
  public abstract static class Builder {
    abstract Builder id(long id);

    abstract Builder user(String user);

    abstract Transaction build();
  }
}

When using it, you can do:

Transaction t = Transaction.builder().id(1).user("foo").build();

Conclusion

In this post, we’ve seen the basics of Google Auto Value library. How to use it to create value class with little effort. It generates the hashCode(), equals(), toString() for you. We also checked the Maven setup. And finally, we learnt a builder pattern for creating complex value class. Hope you enjoy this one, see you next time!

The source code is available on GitHub: https://github.com/mincong-h/auto-value-demo

References