Java 8 or higher is required to run the Immutables annotation processor. Functionality working with records requires Java 17 or above. Works well with Java 21 and Java 25 too (mentioning only LTS releases)
Add the required dependencies for basic immutable object generation:
Snippet of Maven dependencies:
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>2.12.0</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.
This way of working with annotation processing is deprecated (as of JDK 22), and
it doesn’t work with Java Module System. There is workaround for JDK 22+, to use compiler arguments
like this in maven-compiler-plugin.
<compilerArgs>
<arg>-parameters</arg>
<arg>-proc:full</arg>
</compilerArgs>
But it’s also a good idea to switch to annotationProcessorPaths, see below.
Currently, proper, “modern” way is to configure annotation processing using annotationProcessorPaths
and put on the regular dependency classpath only annotations or runtime support jars.
<properties>
<immutables.version>2.12.0</immutables.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<configuration>
<release>21</release>
<!-- Explicit annotation processor path -->
<annotationProcessorPaths>
<path>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId> <!-- contains the annotation processor -->
<version>${immutables.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Only the annotations/API go on the normal compile classpath -->
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value-annotations</artifactId>
<version>${immutables.version}</version>
<scope>provided</scope> <!-- compile only -->
</dependency>
</dependencies>
Annotations and processor jar should be at the same version, compilation might fail if not.
If you are using multiple dependencies like org.immutables:serial, org.immutables:builder, org.immutables:datatype etc., 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.12.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
Then just use dependencies, all automatically aligned to the same version
<dependencies>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>serial</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
JIC Here’s Reference of all modules
Gradle seems to settle nowadays on just using proper builtin scopes
dependencies {
annotationProcessor "org.immutables:value:$versionImmutables"
compileOnly "org.immutables:value-annotations:$versionImmutables"
}
where versionImmutables comes from gradle.properties or just a variable in build script.
You can use libs. or TOML file, or whatever approach to managing libraries and versions
you might like, that was just an example.
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 Classic guide! Or New & Nice for something new and… nice!
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.
Some advanced workarounds (read hacks to overcome limitations or compiler bugs) may work or not work
depending on compiler and build tool, but those are mostly advanced or corner cases.
Gradle or Maven plugins should make it integratable in IDE, but here’s a bit rusty, but still relatively valid guide (with pictures) on manually configuring annotation processing in IDEs Using annotation processor in IDE.
Troobleshooting section moved to a separate page: Troobleshooting
Haven’t found what you looking for? You can take a look into older version of the guide: (Legacy) Get Started!. It may be useful for Android configuration