Immutables

Get started!

Prerequisites

Java 7 or higher is required to run the Immutables annotation processor.

Add the required dependencies for basic immutable object generation:

Snippet of Maven dependencies:

<dependency>
  <groupId>org.immutables</groupId>
  <artifactId>value</artifactId>
  <version>2.10.1</version>
  <scope>provided</scope>
</dependency>

In Maven, the dependency can be declared in the “provided” scope, or made “optional”. The artifact is not required at runtime; it is compile-only dependency.

If you are using multiple dependencies like org.immutables:serial you can import the bill of materials (BoM) in your dependency management and go without specifying the version of the dependency individually:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.immutables</groupId>
      <artifactId>bom</artifactId>
      <version>2.10.1</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

The Immutables annotation processor runs under any Java build tool that uses javac as compiler backend (assuming that annotation processing is not disabled in build tool configuration). The Eclipse JDT compiler (ECJ) also supports this annotation processor. See Using annotation processor in IDE.

Create immutable object

Assuming that required dependencies were added, create an abstract class with abstract accessor methods. You can do the same by annotating with interfaces or even annotations (@interface):

package info.sample;

import java.util.List;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

It is now possible to generate and then use the generated immutable implementation:

package info.sample;

import java.util.List;

public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}

    int foo = value.foo(); // 2

    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}

Congratulations! You’re done! See the sample generated code for an example of what kind of code is being generated by the processor.

Even basic immutable class generation has a lot more tricks to show. Check out the guide!

Read guide…

Configuration for Android

To comfortably use Immutables for Android development, the following steps are required to set up a working build configuration:

Using apt plugin

An annotation processor plugin should be configured for the Android build. Although Immutables annotation processing works with javac without specific a configuration, a plugin is required to ensure that output directories, dependency scopes and other miscellaneous details are being set up correctly. We’ve found that the android-apt plugin works well for this, including interaction with Android Studio.

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath "com.android.tools.build:gradle:1.2.3"
    // add dependency to plugin
    classpath "com.neenbedankt.gradle.plugins:android-apt:1.6"
  }
}
// ...

Then, use apply plugin: 'android-apt' in modules to apply the plugin.

Adding Immutables dependencies

The Immutables annotation processor should be added to the special apt scope (declared by the android-apt plugin) and to the compile-time-only provided scope (declared by the Android Gradle plugins).

apply plugin: "com.android.application"
apply plugin: "android-apt"
// ...
dependencies {
  compile fileTree(dir: "libs", include: ["*.jar"])
  apt "org.immutables:value:2.10.1" // <-- for annotation processor
  provided "org.immutables:value:2.10.1" // <-- for annotation API
}

Other compile-only dependencies applicable to Android should be added in the provided scope. Jar files with annotations such as org.immutables:builder:2.10.1 (see Factory Builders) or org.immutables:gson:2.10.1 (see GSON support) don’t have to be propagated to an Android application, especially if there are no runtime classes needed. For example, the gson module provides optional runtime classes which are not suitable for Android apps.

dependencies {
  // ...
  apt "org.immutables:value:2.10.1" // for annotation processor
  provided "org.immutables:value-annotations:2.10.1" // for annotations
  provided "org.immutables:builder:2.10.1" // for annotations
  provided "org.immutables:gson:2.10.1" // for annotations
}

Since version 2.0.19, for some combined annotation and runtime artifacts there are separate annotation-only artifacts available. For example, the value and gson modules have additional artifacts with annotations classifiers. This allows for the reduction of dependencies and avoids lint warnings about using certain types that are not available on Android (despite not being required at runtime). The above example with the alternative dependencies could be rewritten as:

dependencies {
  // ...
  apt "org.immutables:value:2.10.1" // for annotation processor
  provided "org.immutables:value:2.10.1:annotations" // annotation-only artifact
  provided "org.immutables:builder:2.10.1" // there are only annotations anyway
  provided "org.immutables:gson:2.10.1:annotations" // annotation-only artifact
}

Since version 2.7 in addition to org.immutables:value:annotations artifact (with annotations classifier), also available equivalent org.immutables:value-annotations annotation-only artifact. It will be easier for some tools to consume artifacts without classifiers and including source jars.

Read guide…

Troubleshooting

Immutables itself and the surrounding ecosystem of build tools, compilers and even JVM may contain bugs. Please, try to upgrade to the latest stable version of tools you use if possible and report any issues found.

There’s known issue with the interaction between the incremental compilation feature of javac and annotation processing. Build tools like Maven are also affected by this bug. Typically, commands such as mvn clean compile will resolve any such problems by a forcing full build. Disabling incremental compilation is also an option:


<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.3</version>
  <configuration>
    <compilerVersion>1.8</compilerVersion>
    <source>1.8</source>
    <target>1.8</target>
    <!-- Prevents an endPosTable exception during compilation -->
    <useIncrementalCompilation>false</useIncrementalCompilation>
  </configuration>
</plugin>

Search phrase: “java.lang.IllegalStateException: endPosTable already set”

Sometimes there are too many compilation errors due to generated files not found, that may hinder real annotation processing errors when max error limit is reached. The limit can be configured for javac as -Xmaxerrs 1000000. For Maven it could be set as ... <compilerArguments><Xmaxerrs>1000000</Xmaxerrs></compilerArguments></configuration> for maven-compiler-plugin.

In case of some spurious errors Some internal annotation processing errors may be not clearly reported for Maven, you may rerun build with -X debug output and, possibly, quite verbose compiler configuration:

<compilerArgs>
  <arg>-verbose</arg>
  <arg>-XprintRounds</arg>
  <arg>-XprintProcessorInfo</arg>
  <arg>-Xlint</arg>
  <arg>-J-verbose</arg>
</compilerArgs>