Immutable
Type declaration annotated with @Immutable is not immutable

Severity
ERROR
Has Fix?
REQUIRES_HUMAN_ATTENTION

The problem

This check validates that all classes annotated with Error Prone’s @Immutable annotation (com.google.errorprone.annotations.Immutable) are deeply immutable. It also checks that any class extending an @Immutable-annotated class or implementing an @Immutable-annotated interface are also immutable.

Other versions of the annotation, such as javax.annotation.concurrent.Immutable, are currently not enforced.

An object is immutable if its state cannot be observed to change after construction. Immutable objects are inherently thread-safe.

A class is immutable if all instances of that class are immutable. The immutability of a class can only be fully guaranteed if the class is final, otherwise one must ensure all subclasses are also immutable.

A conservative definition of object immutability is:

The requirement that all reference fields be immutable ensures deep immutability, meaning all contained state is also immutable. A weaker property, common with container classes, is shallow immutability, which allows some of the object’s fields to point to mutable objects. One example of shallow immutability is guava’s ImmutableList, which may contain mutable elements.

It is possible to implement immutable classes with some internal mutable state, as long as callers can never observe changes to that state. For example, some state may be lazily initialized to improve performance.

It is also technically possible to have an immutable object with non-final fields (see the implementation of String#hashCode() for an example), but doing this correctly requires subtle reasoning about safe data races and deep knowledge of the Java Memory Model.

For more information about immutability, see:

Type Parameters

When an @Immutable class has type parameters that are used in the type of that class’s fields, that class is called an immutable generic container. Usages of immutable generic container classes, such as ImmutableList, are only actually deemed immutable if the arguments to all such type parameters are also deemed immutable. For example, an ImmutableList<String> is deemed immutable since Strings are immutable. However, an ImmutableList<Object> is not deemed immutable since Objects are not provably immutable.

When creating generic container classes, Error Prone requires that you declare whether that container is allowed to be used with mutable, or only with immutable type parameters.

@Immutable(containerOf = ...)

If you want to allow your immutable generic container to possibly contain mutable types, use @Immutable’s containerOf method:

@Immutable(containerOf = "T")
class ImmutableHolder<T> {
  final T ref;
  ...
}

Error Prone will allow you to instantiate an ImmutableHolder<String> and use it as a field in another @Immutable class. You may instantiate an ImmutableHolder<Object>, but since it is mutable, Error Prone would report an error if that was a field of another @Immutable class.

@ImmutableTypeParameter

If you want to allow your @Immutable generic container to only contain immutable types, use @ImmutableTypeParameter:

@Immutable
class ImmutableContainer<@ImmutableTypeParameter T> {
  final T ref;
  ...
}

Error Prone will allow you to instantiate a ImmutableContainer<String> and use it as a field in another @Immutable class. However, it is a compiler error to instantiate an ImmutableContainer<Object>.

You can also use @ImmutableTypeParameter to annotate a method’s type parameters:

class SomeMutableClass {
  <@ImmutableTypeParameter T> ImmutableList<T> putInImmutableList(T t) {
    return ImmutableList.of(t);
  }
}

Type Parameters Not Used in Fields

If your @Immutable class has a type parameter that is not used in the type of your class’s fields, then there is no need to use containerOf or @ImmutableTypeParameter:

@Immutable
class NonContainer<T> {
  ... // No fields whose type contains T
  void process(T element) {
    // process 'element', which won't violate NonContainer's immutability.
  }
}

Suppression

Suppress false positives by adding an @SuppressWarnings("Immutable") annotation to the enclosing element, or the offending field.

To suppress warnings in AutoValue classes, add @AutoValue.CopyAnnotations to ensure the suppression is also applied to the generated sub-class:

@AutoValue
@AutoValue.CopyAnnotations
@Immutable
@SuppressWarnings("Immutable")
class MyAutoValue {
  ...
}