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.
NOTE: 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:
this
reference does not escape the
constructor).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.
If you have an immutable class with mutable fields as described above, you can
mark it as such by suppressing the Immutable check on it. This
will allow your class to be included in other @Immutable
classes.
For more information about immutability, see:
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
String
s are immutable. However, an ImmutableList<Object>
is not deemed
immutable since Object
s 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>
.
@ImmutableTypeParameter
can restrict generic parameters to immutable types
only but the generic itself may not be immutable.
class MutableContainer<@ImmutableTypeParameter T> {
...
}
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);
}
}
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.
}
}
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 {
...
}