Immutables

Immutable objects

Overview

To reap the benefits of immutability in Java, we created an annotation processor to easily create simple and consistent value objects. You can think of it as Guava’s Immutable Collections but for regular objects.

The core of Immutables is modelling. Good modelling is at the heart of creating good applications and services, and of good design in general. We feel proud to fill a gap in the area of modelling in the Java programming language, where conventional JavaBeans are insufficient.

Get started! Create an abstract value class and then add annotation to generate an immutable implementation class!

See sample generated code for an example of the code generated by the processor, or jump straight to the features!

Concepts

Abstract value type

An Abstract value type is a manually-written non-final (usually abstract) class or interface (or even annotation type) that defines the value type and is annotated with the org.immutables.value.Value.Immutable annotation. It may contain attributes and other metadata, as well as regular Java methods (and fields, if necessary). It is strongly recommended that abstract value types not introduce visible mutable state. Abstract value types are used as the source model for generated code. Get started!.

Attributes

An attribute holds a value that cannot be changed after the owning object is created. The name “attribute” is used to intentionally distinguish the concept from “fields” or JavaBean “properties”, and to imply a similarity with Java annotation attributes. It is defined by an accessor method: A zero argument, non-void-returning Java method. No annotations are required on abstract accessor methods in order for them to become attributes. However, some attributes such as those with default values are non-abstract methods that have bodies that compute values. Such accessors therefore require special annotations to distinguish them from regular methods.

Immutable implementation class

A Generated final class that extends a manually-written abstract value type and implements all declared accessor methods as well as supporting fields, methods, constructors, and a builder class. An immutable implementation class implements abstract attribute accessors for scalar primitive and object reference types, with special support provided for collection attributes and other types. java.lang.Object’s equals, hashCode, and toString methods are overridden and fully dependent on attribute values rather than on object identity. Immutable implementation classes are the primary (but not the only) source code artifacts generated by the Immutables annotation processor.

Features

Value

The annotation processor works by using annotated abstract value types as a model to generate immutable implementations. A generated implementation extends or implements an abstract value type. Classes don’t have to be abstract if they don’t define abstract accessor methods.

@Value.Immutable
interface ValueInterface {}

@Value.Immutable
class ValueClass {}

@Value.Immutable
@interface ValueAnnotation {}

...

ValueInterface valueInterface = ImmutableValueInterface.builder().build();

ValueClass valueClass = ImmutableValueClass.builder().build();

ValueAnnotation valueAnnotation = ImmutableValueAnnotation.builder().build();

You can customize generated class names to have other prefixes than Immutable* or to have no prefix at all. See styles.

Nested abstract value types should be declared static if declared as inner classes (interfaces and annotations are implicitly static if nested).

You are not limited to using classes that you control. You can generate immutable implementation classes from the abstract types in other packages.

The @Value.Include annotation can be used on types and packages. This is most useful when you want to generate implementations of annotations to use with DI libraries such as Guice. Inclusion can be used in combination with @Value.Enclosing

package my.package;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.immutables.value.Value;

@Value.Include({ Retention.class })
interface IncludedAnnotations {}
...

ImmutableRetention.builder()
    .value(RetentionPolicy.CLASS);
    .build();

Builder

By default, builders are generated for each immutable implementation class. Builders enable expressive construction of objects using named attribute initializers. Generally, builders compensate for the lack of named and optional constructor arguments in the Java language.

The build() method will fail if any mandatory attribute is omitted. Efficient bit masks are used internally to track which attributes are initialized!

// builder methods illustrated
ImmutableValue.builder()
    .foo(1442)
    .bar(true)
    .addBuz("a")
    .addBuz("1", "2")
    .addAllBuz(iterable)
    .putQux(key, value)
    .build();

You can customize initialization methods to have prefixes like set or with, have builders created by constructor, have create methods, etc. See styles.

By default, builders have a method named from. The from method allows for “editing” operations on immutable values by initializing the builder with attribute values taken from an existing immutable object. This could be used to prevent structural sharing as happens with copy-methods, or to accumulate collection elements from attributes of multiple values.


ImmutableValue.builder()
    .from(otherValue) // merges attribute value into builder
    .addBuz("b")
    .build();

The from method on builders is a much more sound and powerful alternative than having toBuilder() (or alike) on immutable values. For strict builders, from methods are not generated as they’re prone to errors. If value object inherits abstract accessor definitions from super-types, than it would be possible to copy from super-type instance (of any implementation type) those attributes which are applicable. Generated from method will have overloads for each super-type from which we can get attribute values we inherit. Just to note: types parameterized with generics and accessors with covariant return type overrides will be excluded from such “from super-type” initialization.

If a particular builder has become redundant due to the presence of a constructor, generation of the builder can be disabled using the @Value.Immutable(builder = false) annotation parameter.

For advanced use cases, it may be desirable to have builders that produce different types of objects but conform to the same interface, akin to the original Builder pattern. This is achievable by declaring a static nested class named “Builder” which will be extended by the generated builder.

interface Vehicle {
}

interface VehicleBuilder {
  // Generated builders will implement this method
  // It is compatible with signature of generated builder methods where
  // return type is narrowed to Scooter or Automobile
  Vehicle build();
}

@Value.Immutable
public abstract class Scooter implements Vehicle {
  public abstract static class Builder implements VehicleBuilder {}
}

@Value.Immutable
public abstract class Automobile implements Vehicle {
  public abstract static class Builder implements VehicleBuilder {}
}

class Builders {
  void buildIt(VehicleBuilder builder) {
    Vehicle vehicle = builder.build();
  }

  void use() {
    buildIt(ImmutableScooter.builder());
    buildIt(ImmutableAutomobile.builder());
  }
}

An explicitly declared abstract “Builder” could specify all needed extends or implements declarations in addition to having convenience methods that will show up on the generated builder. However, special care should be taken in order to maintain the structural compatibility of declared builder super-types and generated builders to prevent compile-time type errors from appearing in the generated code.

Using “forwarding” factory methods and abstract builders, it is possible to hide the generated implementation type and its builder from the API. See this example.

Another structural customization for builders involves having a private immutable implementation class, hidden inside the builder, which is then generated as a standalone top-level builder class in the same package.

@Value.Immutable
@Value.Style(visibility = ImplementationVisibility.PRIVATE)
public interface Person {
  String getName();
  String getAddress();
}

Person person = new PersonBuilder()
  .name("Jim Boe")
  .address("P.O. box 0001, Lexington, KY")
  .build();

From version 2.0.17 onwards, it is possible to extend a [yet-to-be] generated builder to code in the following style:

@Value.Style(visibility = ImplementationVisibility.PACKAGE, overshadowImplementation = true)
//...

@Value.Immutable
public interface Person {
  String name();
  String address();
  // static inner class Builder extends generated or yet to be generated Builder
  class Builder extends ImmutablePerson.Builder {}
}

Person person = new Person.Builder()
  .name("Jim Boe")
  .address("P.O. box 0000, Lexington, KY")
  .build();

While ImmutablePerson (and ImmutablePerson.Builder consequently) is not visible outside of package, Person.Builder inherits and exposes all public methods defined on ImmutablePerson.Builder. The overshadowImplementation = true style attribute makes sure that build() will be declared to return abstract value type Person, not the implementation ImmutablePerson, following metaphor: implementation type will be “overshadowed” by abstract value type. The interesting fact is that the calling bytecode references only Person.Builder and not ImmutablePerson.Builder. From the above example: INVOKEVIRTUAL will target Person.Builder.name, Person.Builder.address and Person.Builder.build methods. Essentially, a generated class becomes implementation detail without much boilerplate which is needed to fully hide implementation behind user-written code.

For other structural and naming style customizations, see the style guide.

Strict Builder

By setting the strictBuilder style parameter (@Value.Style(strictBuilder = true, ...)), you can instruct generated builders to operate in strict mode: only forward-only initialization is possible. In other words, only additive operations are available on collection attributes, and regular attributes can be set only once. Strict builders enable early error detection during initialization, such as misspellings or copy-paste leftovers. This makes builders even more similar to object literal expressions. Strict mode is off by default: regular builders are generated in a way that allows the resetting of previously set values.

@Value.Immutable
@Value.Style(strictBuilder = true)
interface Aar {
  boolean a();
  Integer b();
}

ImmutableAar.builder()
    .a(true)
    .b(1)
    .b(2) // IllegalStateException will be thrown here, 'b' cannot be reinitialized
    .build();

No methods to reset collections are generated on strict builders. Additionally, no from method is generated. Those methods would be error-inducing in strict mode.

Note that it is not recommended to use @Value.Style directly on abstract value type; use it directly only during experimentation. The preferred method to use Style annotations is to create meta-annotations as described in the style guide.

Staged builder

The experimental new feature (since 2.3.2) allows you to enable generation of “staged builders” (aka “telescopic builders”). The mode is activated using @Value.Style(stagedBuilder = true) style attribute. A staged builder is a compile-time safe way to ensure that all required attributes are set. The API, composed of stage interfaces, forces initialization of mandatory attributes in stages, one by one, guiding via code-completion and making it impossible to even call build() before all are set. This guarantees that final build() call will not throw IllegalStateException for missing attributes. All remaining optional, nullable and collection attributes can be initialized on a final stage in any order. An addition, removal or change of the source order of the required attributes will cause compilation error for all builder usages and have to be corrected.

@Value.Style(stagedBuilder = true)
@Value.Immutable
public interface Person {
	String name();
	int age();
	boolean isEmployed();
}
...
ImmutablePerson.builder()
    .name("Billy Bounce")
    .age(33)
    .isEmployed(false)
    .build();
...
// under the hood
public final class ImmutablePerson implements Person {
  ...
  public static NameBuildStage builder() { ... }
  public interface NameBuildStage { AgeBuildStage name(String name); }
  public interface AgeBuildStage { IsEmployedBuildStage age(int age); }
  public interface IsEmployedBuildStage { BuildFinal isEmployed(boolean isEmployed); }
  public interface BuildFinal { ImmutablePerson build(); }
}

The price to pay for the additional compile-time safety is the increased count of java interfaces, generated per each required attribute. If staged builders are used extensively, this may lead to the increased memory/disc footprint and can affect class-loading time.

The staged builder mode also implies strict builder mode.

Constructor method

As an alternative to builders, it is possible to provide concise “constructor” factory methods. A “constructor” will be available as a static method named of, on generated immutable implementation classes.

In order to generate a constructor method, certain attributes have to be annotated with org.immutables.value.Value.Parameter annotations.

@Value.Immutable
public abstract class HostWithPort {
  @Value.Parameter
  public abstract String hostname();
  @Value.Parameter
  public abstract int port();
}
...
HostWithPort hostWithPort = ImmutableHostWithPort.of("localhost", 8081);

boolean willBeTrue = hostWithPort.equals(
    ImmutableHostWithPort.builder()
        .hostname("localhost")
        .port(8081)
        .build());

You can optionally specify the ordering of parameters using order annotation attributes, to ensure that the order of constructor parameters does not differ between Java compilers (Java compilers do not necessarily preserve the order of declarations). Source ordering currently works for javac and Eclipse JDT compilers.

@Value.Immutable
public abstract class HostWithPort {
  @Value.Parameter(order = 2)
  public abstract int port();
  @Value.Parameter(order = 1)
  public abstract String hostname();
}
...
HostWithPort hostWithPort = ImmutableHostWithPort.of("localhost", 8081);

If you want to automatically turn all attributes into parameters to generate constructor, you could use styles for that, see tuple style pattern.

Plain public constructor

If there’s a need for plain public constructor instead of the factory method we can achieve it using styles by “renaming” of method to new:

@Value.Style(
  of = "new", // renames "of" method to "new", which is interpreted as plain constructor
  allParameters = true // unrelated to the line above: every attribute becomes parameter
  // reminder: don't get used to inline styles, read style guide!
)
@Value.Immutable
public interface HostWithPort {
  String host();
  int port();
}

HostWithPort hostWithPort = new ImmutableHostWithPort("localhost", 8081);

Things to be aware of

Array, Collection and Map attributes

Following collection types enjoy built-in support for convenient usage:

Array attributes are cloned for safety (due to the mutable nature of Java arrays). Collection attributes are backed by Guava immutable collections if Guava is available on the classpath. Otherwise, they are safely copied and wrapped in unmodifiable collection classes from the standard JDK.

java.util.Set and java.util.Map with enum keys are backed by efficient EnumSet and EnumMap implementations.

Ordered maps and sets are recognized for natural and reverse natural ordering using @Value.NaturalOrder and @Value.ReverseOrder annotations, respectively.

Without ordering annotations, ordered sets and maps attributes will be generated as regular attributes to support construction with custom comparators, etc.

All mentioned above collection types could be also declared as Guava’s immutable collections com.google.common.collect.Immutable*. Please note that other collection implementations such as java.util.ArrayList will not be recognized as special collection attributes.

When building using builders, the contents of collection attributes can be left unspecified. It is possible to verify, for example, that they contain a required number of items using Precondition check methods.

Builders have special methods to initialize collection attributes:

From version 0.16 onwards, we no longer generate clear* methods on builders, so clearFoo() or clearBar() would not be generated for collection or map attributes. To clear the contents of collections or maps, use a reset method bar(Collections.emptyList()), or use copy methods right after an instance is built.

Since version 2.1.11 you can use opt-in depluralization to generate methods named addFoo or putFoo derived from collection attribute named foos.

The set of methods was chosen to represent the minimum required for convenient use. A smaller selection of methods resulted in noisy conversions all over the code using the generated types. A bigger selection of methods resulted in a kitchen-sink effect (in effect, duplicating a mutable collection API!). If you are concerned with the number of methods, consider using tools like ProGuard to remove unused generated methods in the resulting application.

Why are other kinds of containers not supported in the same way? What about java.lang.Iterable, java.util.Collection or java.util.Queue? Those other containers are either too-generic or too-specific for the purposes of immutable object modelling. This might change upon request, of course, and this is what happened with ordered sets and maps (which were recognized with order annotations). On the plus side, any type is supported as an attribute value. Even though there isn’t any kind of magic support, other container types are still usable:

@Value.Immutable
public abstract class DoItYourselfContainer {
   public abstract Iterable<String> iterable();
}
...
ImmutableDoItYourselfContainer.builder()
    .iterable(ImmutableSet.of("a", "b", "c"))
    .build();

Optional attributes

An attribute declared with a return type of com.google.common.base.Optional<T> defines a logically optional attribute of type T.

As of version 2.0, java.util.Optional, java.util.OptionalInt, java.util.OptionalLong, java.util.OptionalDouble from Java 8 are also fully supported.

As of version 2.1.1, com.atlassian.fugue.Option and io.atlassian.fugue.Option are also supported.

Optional values can be omitted when building objects, and will default to Optional.absent() (or Optional.empty() in Java 8). Generated builders have special initializers for optional attributes:

import java.util.*;

@Value.Immutable
public interface AllOptional {
  com.google.common.base.Optional<Integer> v1();
  Optional<Integer> v2();
  OptionalInt i1();
  OptionalLong l1();
  OptionalDouble d1();
}
...
// No error as all values are optional
ImmutableAllOptional.builder().build();

ImmutableAllOptional.builder()
    .v1(1)
    .v2(2)
    .i1(1)
    .l1(1L)
    .d1(1.0)
    .build();

Default attributes

Attributes can have default values provided when none are specified to a builder. To declare a default attribute value, create a non-abstract attribute initializer method and annotate it with org.immutables.value.Value.Default. If the value is omitted during construction, this initializer method will be called to retrieve a default value for the attribute.

@Value.Immutable
public abstract class PlayerInfo {

  @Value.Parameter
  public abstract long id();

  @Value.Default
  public String name() {
    return "Anonymous_" + id();
  }

  @Value.Default
  public int gamesPlayed() {
    return 0;
  }
}
...

PlayerInfo veteran = ImmutablePlayerInfo.builder()
    .id(1)
    .name("Fiddler")
    .gamesPlayed(99)
    .build();

PlayerInfo anonymous44 = ImmutablePlayerInfo.of(44);

String name = anonymous44.name(); // Anonymous_44

Since version 2.1, a default attribute initializer method’s body can refer to other default or derived attributes as long as this does not introduce initialization cycles. If a cycle is introduced, then an IllegalStateException will be thrown pointing to attributes which form a cycle.

There’s no need to use @Value.Default to return empty collections as collection attributes are empty by default if not initialized. Since version 2.2. @Value.Default and @Nullable collection attributes are supported so you can provide default value if no values have been provided but empty collection or null (if nullable) can be set.

For immutable annotation types, default attributes are defined by using the default keyword and will have corresponding default constant values initialized if not set.

Default attributes work well with Java 8’s default methods in interfaces, but attributes should be annotated with @Default:

@Value.Immutable
interface Val {
  int anAttribute();
  @Value.Default default int otherAttribute() {
    return 0;
  }
}

Since version 2.0.15, the defaultAsDefault style parameter (@Value.Style(defaultAsDefault = true, ...)) is supported. This instructes generated builders to treat Java 8 default methods in interfaces as if they were annotated with @Value.Default.

Derived attributes

Derived attributes are attributes with values that are read from existing immutable instances, but cannot be manually set.

To declare a derived attribute, create a non-abstract attribute initializer method and annotate it with org.immutables.value.Value.Derived. In a similar manner to default attributes, the body of the method should compute and return value of an attribute. Derived attributes act much like regular methods that simply compute and return a value, but with a single important difference: values of derived attributes are computed once and stored (at the end of object construction).

@Value.Immutable
public abstract class Order {

  public abstract List<Item> items();

  @Value.Derived
  public int totalCount() {
    int count = 0;

    for (Item i : items())
      count += i.count();

    return count;
  }
}

Order order = ImmutableOrder.builder()
    .addItems(Item.of("item1", 11))
    .addItems(Item.of("item2", 22))
    .build();

// total count will be already computed
int totalCount33 = order.totalCount();

As with default attributes, derived attribute initializer method bodies can refer to other default or derived attributes as long as there are no cycles. If a cycle is detected during object construction, then an IllegalStateException will be thrown pointing to the attribute names which form the cycle.

Nullable attributes

The use of nullable attributes is discouraged. If nullable attributes are really needed, add a @Nullable annotation to the abstract attribute accessor. Any annotation with simple name Nullable will work. Nullable attributes are not required to be set using a builder, and null values are permitted to initialize them. Nullable collections and other special types are not supported. More precisely, adding @Nullable turns an attribute into a “nothing-special” attribute.

@Value.Immutable
interface NullAccepted {
  @Nullable Integer i1();
  @Nullable Long l2();
}

NullAccepted obj = ImmutableNullAccepted.builder()
    .i1(null)
    .build();

obj.toString(); // NullAccepted{i1=null, l2=null}

Nulls in collection

As already mentioned, neither collection, nor its elements are supposed to be null. But for the reason of compatibility with the third party libraries or services you may need to allow or to skip (i.e. throw away silently) nulls. Collection attributes could be marked as @Nullable, but what about collection elements? In this case you can mark attribute with special annotations: @AllowNulls or @SkipNulls. These annotations are not supplied by Immutables and any annotations matching by a simple name will take effect — we call this approach BYOA (Bring Your Own Annotations). Please note, that Guava immutable collections do not support nulls, so this feature is only enabled when JDK collections are used, i.e. when Guava not available or @Style(jdkOnly = true).

@Value.Style(jdkOnly = true)
@Value.Immutable
public interface NullElements {
  // collection elements
  @AllowNulls List<Void> al();
  @SkipNulls List<String> sk();
  // map values (but not keys)
  @AllowNulls Map<String, Integer> bl();
  @SkipNulls Map<String, Integer> sm();
}

It is also possible to use @Nullable, @AllowNulls, @SkipNulls as Java 8 type annotation, like List<@Nullable Obj>, but it may not work depending on compiler (works in ECJ and ErrorProne, but not in plain Javac).

Lazy attributes

A lazy attribute is an initializer method that computes a value lazily and only once.

To declare a lazy attribute, create a non-abstract attribute initializer method and annotate it with org.immutables.value.Value.Lazy. Similar to derived attributes, the body of the method should compute and return a value of an attribute. Lazy attributes act much like regular methods, but compute values the first time they are accessed and return the same memoized value on subsequent accesses.

Things to be aware of

@Value.Immutable
public abstract class Order {

  public abstract List<Item> items();

  @Value.Lazy
  public int totalCost() {
    int cost = 0;

    for (Item i : items())
      cost += i.count() * i.price();

    return cost;
  }
}

Order order = ImmutableOrder.builder()
    .addItems(Item.of("item1", 11, 1))
    .addItems(Item.of("item2", 22, 2))
    .build();

// total cost will be computed now
int lazilyComputedCost = order.totalCost();
// total cost is already computed and stored value is returned
lazilyComputedCost = order.totalCost();

Lazy values are thread-safe and will be computed once and only once, regardless of race conditions.

Unlike default or derived attributes, body of the lazy attribute accessor method could refer to any attribute. If you call lazy attribute during initialization of a default or a derived attribute, it will be initialized eagerly, making it an equivalent of a derived attribute.

Current implementation of lazy attributes is very similar to the way they were implemented in older versions of Scala. Currently, this implementation strategy potentially suffers from the problem described in Scala SIP-20. On the other hand, problems can only occur if you are mixing immutable objects with mutable/static/thread-local state: cyclic dependencies need to be introduced between different immutable objects.

Precondition check method

One of the core advantages of immutable objects is the fact that an immutable object is constructed with proper attribute values in a consistent state, and never changes afterwards. Sometimes, however, a need arises to check attribute values or a combination of attribute values for correctness (cross validation).

Normally, these checks would be written in the constructor of a hand-written class. However, given that there is no hand-written constructor in an immutable implementation class, it is necessary to specify these checks elsewhere. A non-private method annotated with @Value.Check can be used to specify preconditions for generated classes:

@Value.Immutable
public abstract class NumberContainer {
  public abstract List<Number> nonEmptyNumbers();

  @Value.Check
  protected void check() {
    Preconditions.checkState(!nonEmptyNumbers().isEmpty(),
        "'nonEmptyNumbers' should have at least one number");
  }
}
...
// will throw IllegalStateException("'nonEmptyNumbers' should have at least one number")
ImmutableNumberContainer.builder().build();

However, one should note how this differs from other kinds of object state validation where objects may be constructed with values and later validated for correctness regarding business rules in some context: Precondition checking should not be used to validate against such rules, but should be used to preserve consistency and guarantee that the instances are usable.

Precondition check methods are executed when immutable objects are instantiated and all attributes are initialized, but before being returned to a caller. Any instance that fails the precondition checks is made unreachable to a caller due to an exception being raised.

Normalization

There’s additional variant of using @Value.Check annotation to compute normalized value. If you declare return type of validation method with the return type specified as abstract value type, this validation method will also be able to return substitute “normalized” instance. Normalized instance should always be of the immutable implementations type, otherwise ClassCastException will occur during construction.

Be warned that it’s easy to introduce unresolvable recursion, if normalization is implemented without proper checks or with conflicting checks. Always return this if a value do not require normalization.

@Value.Immutable
public interface Normalized {
  int value();

  @Value.Check
  default Normalized normalize() {
    if (value() == Integer.MIN_VALUE) {
      return ImmutableNormalized.builder()
          .value(0)
          .build();
    }
    if (value() < 0) {
      return ImmutableNormalized.builder()
          .value(-value())
          .build();
    }
    return this;
  }
}

int shouldBePositive2 = ImmutableNormalized.builder()
    .value(-2)
    .build()
    .value();

Copy methods

with* methods (withers) allow to modify values of attributes by returning a new immutable object with new value applied and the rest of attributes unchanged.

counter = counter.withValue(counter.value() + 1)

A cheap reference equality == check is added to prevent a copy of the same value by returning this. Primitives are compared using the == value check. Primitive float and double are compared strictly by using Float.floatToIntBits and Double.doubleToLongBits respectively, consistently with how Float.equals and Double.equals work. For strings and primitive wrapper types we use Object.equals equality. But in general, full equality checks were omitted: in practice it may be less computationally expensive to create new copy of a value than to check some attribute for deep-equality.

Wither methods are implemented to copy with structural sharing. It is useful to change one attribute value, but have other attributes values reference the same values as before, including any immutable collections and nested values that are wasteful to rebuild. New values will effectively share the subgraphs of old values, which is desirable in many cases.

While it was tempting to generated a bunch of methods to support collections and maps such as withItemAdded or withKeyValuePut, they might require a lot of variation like add last or add first and will hide the fact that immutable collections are being rebuilt and/or rehashed, which is not always desirable for immutable collections. As of now, there’s only simple value replacement for all kinds of attributes. New collection values are guaranteed to be copied as immutable unless already immutable.

Value changedValue =
    ImmutableValue.copyOf(existingValue)
        .withName("Changed Name")
        .withValues(Arrays.asList("Only new value")) // replacing any copied collection

Copy methods are generated by default, and to use them you need obtain a reference to an immutable implementation class instance, rather than an up-casted abstract value type reference.

Setting the @Value.Immutable(copy = false) annotation parameter will disable the generation of copy methods.

Singleton instances

It is easy to create “empty” or “default” instances that will be singletons. Use the @Value.Immutable(singleton = true) annotation parameter to generate singleton instances. Use the concise of() factory method to obtain a singleton instance.

@Value.Immutable(singleton = true)
public abstract class Data {
  public abstract Set<String> chunks();
}

...
boolean willBeTrue =
    ImmutableData.of() == ImmutableData.of();
// true

boolean willBeTrueAlso =
    ImmutableData.of().chunks().isEmpty();
// true

The abstract value type of a singleton should not have any mandatory attributes, otherwise the generation of singletons will not be possible. You can make attributes non-mandatory by using default or optional attributes.

As it stands, empty singleton instances can be combined with builders and constructors as long as all attributes are non-mandatory. If you only want to provide a singleton instance, disable builders and constructors:

@Value.Immutable(singleton = true, builder = false)
public abstract class Singleton {
  // Limit constructor accessibility to a package if needed.
  // Private access will not work as ImmutableSingleton should
  // be able to extend Singleton
  Singleton() {}
}

...
Singleton singleInstance = ImmutableSingleton.of();

Things to be aware of

Instance interning

There are cases where a number of values of a given type are expected to be finite and measurable performance improvements may be gained by interning those instances.

If all you need is to strongly intern all instances of particular value type — Immutables can do that for you. Use the @Value.Immutable(intern = true) annotation parameter to enable strong interning:

Strong interning is supported by default and since version 2.6.0 weak interning is also supported via @Value.Style(weakInterning = true).

Any other forms of interning including partial range interning were left out to be implemented externally. There is, however, a module org.immutables:ordinal which supports sophisticated domain-based interning of enum-like objects. See the documentation of classes in that module.

Precomputed hashCode

If an immutable class has a lot of attributes, or attributes may contain reasonably large object graphs, then it may become inefficient to recompute hashCode value again and again (when inserting instances into a HashMap, for example).

For such cases, hash codes can be precomputed on construction and stored for fast retrieval. Just use the @Value.Immutable(prehash = true) annotation parameter to precompute hash values in advance.

Lazy computation of hashCode

Similarly to prehashed hash codes, one can configure immutables to calculate hash code lazily. Hash value will be computed (and cached) on first call to hashCode() method. The approach is similar to hashCode() function in String class (also called racy single check in Effective Java book). Note that lazy-hash approach doesn’t use synchronization/volatile primitives and double calculation is possible under certain race conditions.

The difference between prehash and lazyhash is that former calculates hashCode eagerly (in object constructor) while later does it lazily (in hashCode() method).

Redacted attributes

Since v2.5.0 Value.Redacted annotation was introduced to hide or mask attribute values from auto-generated toString method. This can be useful to make sure PII will not leak to logs or something like that. It will be just excluded by default. However, you can choose to put special masking characters next to the attribute instead of characters, like 3 asterisks or 4 pound signs: a replacement string can be set using redactedMask style attribute.

@Value.Style(redactedMask = "####")
..
@Value.Immutable
public interface RedactedMask {
  @Value.Redacted
  String ssn();
  @Value.Redacted
  String secret();
}
// toString output: RedactedMask{ssn=####, secret=####}
// without setting style it would be just: RedactedMask{}

See opaque containers as the more disciplined, customizable and type-safe way to achieve the same. Or see yet another alternative in the form of auxiliary attributes just below.

Auxiliary attributes

Sometimes it is desirable to exclude an attribute from the generated equals, hashCode, and toString methods.

Attributes annotated with @Value.Auxiliary will be stored and will be accessible, but are excluded from equals, hashCode and toString method implementations. Lazy attributes are always acting as auxiliary.

@Value.Immutable(intern = true)
interface TypeDescriptor {
  // Instances are interned only by qualified name
  String qualifiedName();
  @Value.Auxiliary
  TypeElement element();
}

If this is not enough, hashCode, equals, and toString methods can be customized directly.

Customize toString, hashCode and equals

It’s quite easy to customize the generated toString, hashCode and equals methods. The Immutables processor will simply use provided non-abstract definitions for those methods if any are present in an abstract value type:

@Value.Immutable
public abstract class OmniValue {
  @Override
  public boolean equals(Object object) {
    return object instanceof OmniValue;
  }

  @Override
  public int hashCode() {
    return 1;
  }

  @Override
  public String toString() {
    return "OmniValue{*}";
  }
}

boolean willBeTrue =
    ImmutableOmniValue.builder().build()
        .equals(new OmniValue() {});

Additionally, manually written equals and hashCode methods will automatically work correctly with instance interning and precomputed hashCode features!

Obviously, you should only provide manual implementations of the equals and hashCode methods if you really know what you are doing!

If you just want exclude some attributes from the generated equals and hashCode methods, a simpler alternative is to mark them as auxiliary instead of writing your own custom implementations.

Immutable Annotation

Annotations types can also be annotated as @Value.Immutable. Immutable annotation implementations will be generated which behave according to the specification. You can expect array, default attributes, and all other features to work in the same way as for regular immutable objects.

@Value.Immutable
public @interface MyAnnotation {
  String[] value();
  boolean enable() default true;
}
...
ImmutableMyAnnotation.builder()
  .addValue("a", "b")
  .enable(true)
  .build();

If annotations reside in a different library or package, you still can generate implementations and builders using the Include annotation.

Serialization

Basic Java binary serialization is supported in the following way:

Advanced Java binary serialization annotations are available in the serial module (since v2.0.12):

For JSON serialization options see the JSON guide.

Modifiable classes

While Immutables is heavily biased towards immutability, limited support for mutable implementations is also provided.

Use the annotation @Value.Modifiable with or without a corresponding @Value.Immutable. The generated mutable companion class will have the prefix Modifiable. It is more limited and arguably more difficult to get right semantically, but it may be useful as a buffer, uber-builder, or partially-initialized implementation. We believe that modifiable companion class is a better alternative to:

@Value.Immutable
@Value.Modifiable
interface Item {
  String getName();
  List<Integer> getCount();
}
...

// When simple workflow of regular Builder is not enough

ModifiableItem item = ModifiableItem.create()
    .setName("Super Item")
    .addCount(1)
    .addCount(2);

item.getCount().add(3);

if (item.isIntialized()) {
  ImmutableItem immutableItem = item.toImmutable();
  System.out.println(immutableItem);
  // Item{name=Super Item, count=[1, 2, 3]}
}

item.clear()
    .from(ImmutableItem.builder().name("First").addCount(4, 5).build())
    .from(ImmutableItem.builder().name("Second").addCount(6).build());

System.out.println(item);
// ModifiableItem{name=Second, count=[4, 5, 6]}

The naming conventions of modifiable classes can be changed using styles, even going as far as creating builders in disguise.

Generics are fully supported!

Starting with version 2.2 generic parameters are supported and can have upper bounds specified if needed.

interface TreeElement<T> {}

@Value.Immutable
interface Node<T extends Serializable> extends TreeElement<T> {
  List<TreeElement<T>> elements();
}

@Value.Immutable
interface Leaf<T extends Serializable> extends TreeElement<T> {
  @Value.Parameter T value();
}

TreeElement<String> tree =
    ImmutableNode.<String>builder()
        .addElements(ImmutableLeaf.of("A"))
        .addElements(ImmutableLeaf.of("B"))
        .addElements(
            ImmutableNode.<String>builder()
                .addElements(ImmutableLeaf.of("C"))
                .addElements(ImmutableLeaf.of("D"))
                .build())
        .build();

See Wrapper types for other examples illustrating the use of generics.

Annotation injection

Experimental annotation injection introduced in version 2.6.0: allows injecting annotation on fields, accessors, initializer, immutable and builder types etc. Injection directives are defined as custom annotations meta-annotated with @InjectAnnotation in new org.immutables:annotate module. Allow some non-trivial annotation code construction, see Javadoc of @InjectAnnotation for details.

The motivation of annotation injection is integration with some introspection-based toolkits and frameworks.

Warnings

The Immutables annotation processor have dozens of checks and issues errors and warning where it’s impossible to generate certain elements or certain features are error prone and discouraged. When you use @Nullable on a primitive or when one Value.Immutable type extends another Value.Immutable, you’ll get an error. For missing, superfluous or ignored annotations or discouraged combinations, you will get warnings. You can suppress such warnings using SuppressWarnings("immutables") or SuppressWarnings("all") on an element or its enclosing elements.

See also Style.generateSuppressAllWarnings style attribute to adjust warnings in the generated code.


Patterns ——–

This section contains common patterns and recipes using Immutables that are useful but are not actually features by themselves.

Wrapper types

Very often we’re creating wrapper types around primitives, strings, and commonly used types to radically improve type safety. We definitely don’t want to have unsafe “stringly typed” code all over the place.

However, types should also introduce the absolute minimum of syntactic overhead: If such wrapper types are easy to create and use, then fewer accidental errors will end up in code.

Use a supertype and corresponding styles to describe your wrapper types:

// declare style as meta annotation as shown
// or on package/top-level class
// This is just an example, adapt to your taste however you like
@Value.Style(
    // Detect names starting with underscore
		typeAbstract = "_*",
    // Generate without any suffix, just raw detected name
		typeImmutable = "*",
    // Make generated public, leave underscored as package private
		visibility = ImplementationVisibility.PUBLIC,
    // Seems unnecessary to have builder or superfluous copy method
		defaults = @Value.Immutable(builder = false, copy = false))
public @interface Wrapped {}

// base wrapper type
abstract class Wrapper<T> {
  @Value.Parameter
  public abstract T value();
  @Override
  public String toString() {
    return getClass().getSimpleName() + "(" + value() + ")";
  }
}

...
// Declare wrapper types/domain values

@Value.Immutable @Wrapped
abstract class _LongId extends Wrapper<Long> {}

@Value.Immutable @Wrapped
abstract class _PersonName extends Wrapper<String> {}

@Value.Immutable @Wrapped
abstract class _VehicleMake extends Wrapper<String> {}

...
// Enjoy your wrapper value types

LongId id = LongId.of(123L);

PersonName name = PersonName.of("Vasilij Pupkin");

VehicleMake make = VehicleMake.of("Honda");

You can make it so that only @Wrapped is needed without corresponding @Value.Immutable annotation, if you follow the recipe for custom immutable annotations.

Tuple style

Using styles you can create types with only constructor generated which includes all attributes as parameters. The key style here is allParameters which automatically makes constructor for all parameters regardless if they are annotated with @Value.Parameter.

@Value.Style(
    // Generate construction method using all attributes as parameters
    allParameters = true,
    // Changing generated name just for fun
    typeImmutable = "*Tuple",
    // We may also disable builder
    defaults = @Value.Immutable(builder = false))
public @interface Tuple {}
...
// declare type with "tuple" style
@Value.Immutable @Tuple
public interface Complex {
  double re();
  double im();
}
...

Complex c = ComplexTuple.of(1d, 0d);

You can make it so that only @Tuple is needed without corresponding @Value.Immutable annotation, if you follow the recipe for custom immutable annotations.

Wrapper/Tupple initializers inlined as alternative setters with Deep Immutables Detection

When both the Wrapper (i.e. single) and the Tupple (i.e. multiple) value objects are used in another @Immutable, it can be useful to have short-cuts to avoid having to explicitly build the trivial contained inner value object when constructing the outer object. This is possible with the deepImmutablesDetection style:

@Value.Immutable
@Value.Style(deepImmutablesDetection = true, depluralize = true)
public interface Line {
  List<Point> points();
}

@Value.Immutable
@Value.Style(allParameters = true)
public interface Point {
  int x();
  int y();
}

ImmutableLine line = ImmutableLine.builder()
  .addPoint(1, 2) // implicit addPoint(ImmutablePoint.of(1, 2))
  .addPoint(4, 5)
  .build();
}

Expressive factory methods

There were feature requests to customize the names of constructor methods and, in addition, provide construction hooks. We identified two interconnected needs:

Having considered special annotation parameters and hook-methods, we eventually came up with… Nothing. This, surprisingly, solves the problem and requires no features: Simply declare factory methods on abstract value classes and forward calls to constructor methods of immutable implementation classes.

@Value.Immutable
public abstract class Point {
  @Value.Parameter
  public abstract double x();
  @Value.Parameter
  public abstract double y();

  public static Point origin() {
    return ImmutablePoint.of(0, 0);
  }

  public static Point of(double x, double y) {
    return ImmutablePoint.of(x, y);
  }

  public static Point fromPolar(double r, double t) {
    return ImmutablePoint.of(r * Math.cos(t), r * Math.sin(t));
  }
}

You may also want to use forwarding factory methods to hide the implementation class from the surface of an abstract value type API. In example above, notice how the use of ImmutablePoint does not leak through Point’s public interface.

Hide implementation class

In addition to the examples above, it’s also possible to hide builder implementations in the same manner using nested abstract Builders. While it increases verbosity, it means that implementation classes are not exposed as a public API:

// Make generated class package private
@Value.Style(visibility = ImplementationVisibility.PACKAGE)
@Value.Immutable
public abstract class Point {
  @Value.Parameter public abstract double x();
  @Value.Parameter public abstract double y();

  public static Point of(double x, double y) {
    return ImmutablePoint.of(x, y);
  }

  public static Builder builder() {
    return ImmutablePoint.builder();
  }
  // Signatures of abstract methods should match to be
  // overridden by implementation builder
  public interface Builder {
    Builder x(double x);
    Builder y(double y);
    Point build();
  }
}

Smart data

Immutable objects act very well in the role of “smart data”: In addition to being a pure data container, a value object can also carry domain-specific knowledge and the capability to perform computations. While services and entities orchestrate the execution of business logic, value objects handle computations that are specific to problem domain, but agnostic to any particular business context.

@Value.Immutable
public abstract class OriginDestination {
  @Value.Parameter
  public abstract Airport origin();
  @Value.Parameter
  public abstract Airport destination();

  public boolean isDomestic() {
    return origin().country().equals(destination().country());
  }

  public boolean isCrossCityTransit() {
    return origin().city().equals(destination().city());
  }

  public OriginDestination reverse() {
    return ImmutableOriginDestination.of(destination(), origin());
  }
  ...
}

Go ahead! Enrich value objects with methods that compute values — push computation complexity to the right place!

Non-public attributes

Particular attributes may become redundant from the standpoint of the public interface of an abstract value class. Lowering the visibility may help to hide attribute from API consumers, however it will still be exposed as public on builders and as constructor parameters:

@Value.Immutable
public abstract class Name {
  @Value.Parameter
  abstract String value();

  public String toString() {
    return value();
  }

  public static Name of(String value) {
    return ImmutableName.of(value);
  }
}
...

Name name = Name.of("The Rose");
String value = name.toString();
// "The Rose"

Null-Object pattern in attribute values

As an alternative to using Optional<T> attributes, the null-object pattern could be used. This requires no special support from Immutables; just use default attributes:

public enum Stars {
  NONE, ONE, TWO, THREE, FOUR, FIVE;
}

@Value.Immutable
public abstract class Hotel {
  @Value.Default
  public Stars stars() {
    return Stars.NONE;
  }
}

Opaque containers

It may sometimes be necessary to prevent fields from being exposed via the toString method, or to handle hashCode or equals in a very specific manner. It’s always better to avoid introducing ad-hoc features into the annotation processor to achieve this! For example, if you want to mask some confidential data from a toString method, create an opaque wrapper for this data:

@Value.Immutable(builder = false)
abstract class Confidential {
  @Value.Parameter
  abstract String value();
  public String toString() { return "<NON DISCLOSED>"; }
}

Then, it can be used safely as an attribute:

@Value.Immutable
interface Val {
  int number();
  Confidential confidential();
}
...
// toString
"Val{number=1, confidential=<NON DISCLOSED>}"

See also redacted attributes for hiding or masking attribute values in generated toString. See also auxiliary attributes for excluding attribute from hashCode, equals, toString