| All fields in your enum class should be final and either be primitive or refer | |
| to deeply immutable objects. | |
| Static state is dangerous to begin with, but much worse for enums. We all think | |
| of enum values as constants -- and even refer to them as "enum constants" -- and | |
| would be very surprised if any of their state ever changed, or was not | |
| thread-safe. | |
| To make enums immutable, ensure: | |
| * All fields are final. | |
| * The types of all fields of the enum are deeply immutable. For example, use | |
| `ImmutableList` and `ImmutableSet` instead of `List` and `Set`. | |
| * Types are considered immutable if they are primitives, in a set of types | |
| that are built in to Error Prone (e.g. `java.lang.String`, | |
| `java.util.UUID`), or are annotated with | |
| `com.google.errorprone.annotations.Immutable`. | |
| Other versions of the annotation, such as | |
| `javax.annotation.concurrent.Immutable`, are currently *not* recognized. | |
| See https://errorprone.info/bugpattern/Immutable for additional discussion. | |
| * If the type you're using inside the enum can be annotated with | |
| `@Immutable`, you should do that: | |
| ```java | |
| // WARNING: E is not immutable, since MyValueObject isn't Immutable | |
| enum E { | |
| private final MyValueObject mvo = new MyValueObject(); | |
| } | |
| // Add @Immutable here | |
| final class MyValueObject {} | |
| ``` | |
| Note that MyValueObject must actually be immutable. The | |
| [Immutable](Immutable.md) checker will raise a compile-time error if the | |
| `MyValueObject` class isn't actually immutable. | |
| * If the type you're using inside the enum is not considered immutable, | |
| and you can't annotate the type because it's outside the project, | |
| consider using an immutable replacement of the type, or | |
| [suppress this check on the enum](#suppression) with a comment about why | |
| the fields in question are immutable. | |
| TIP: annotating the declaration of the enum class with `@Immutable` is | |
| unnecessary -- Error Prone assumes enums are immutable by default. | |
| Example: | |
| ```java | |
| import com.google.errorprone.annotations.Immutable; | |
| @Immutable | |
| class Foo { | |
| final int id; | |
| public Foo(int id) { | |
| this.id = id; | |
| } | |
| } | |
| ``` | |
| ```java | |
| // The declaration doesn't need to be annotated with @Immutable. | |
| enum E { | |
| A("A", ImmutableList.of(new Foo(1), new Foo(2))), | |
| B("B", ImmutableList.of(new Foo(3))); | |
| // All fields are final, and deeply immutable: | |
| // Error Prone knows String is immutable. | |
| private final String name; | |
| // Error Prone knows ImmutableList<T> is an immutable collection of some | |
| // objects, and it recognizes the @Immutable annotation on the declaration of | |
| // Foo, so it can safely determine that this ImmutableList is deeply | |
| // immutable. | |
| private final ImmutableList<Foo> foos; | |
| private E(String name, ImmutableList<Foo> foos) { | |
| this.name = name; | |
| this.foos = foos; | |
| } | |
| public ImmutableList<Foo> foos() { | |
| return foos; | |
| } | |
| public String name() { | |
| return foos; | |
| } | |
| } | |
| ``` | |
| TIP: Instead of creating an enum with functional interface fields (`Predicate`, | |
| `Function`, etc.), declare abstract methods that are overridden by each | |
| constant. For example, do this: | |
| ```java | |
| enum Types { | |
| STRING { | |
| @Override public boolean hasCompatibleType(Object o) { | |
| return o instanceof String; | |
| } | |
| }, | |
| NUMBER { | |
| @Override public boolean hasCompatibleType(Object o) { | |
| return o instanceof Number; | |
| } | |
| }, | |
| // ... | |
| public abstract boolean hasCompatibleType(Object o); | |
| } | |
| ``` | |
| ... not this: | |
| ```java | |
| enum Types { | |
| STRING(o -> o instanceof String), | |
| NUMBER(o -> o instanceof Number), | |
| // ... | |
| final Predicate<Object> hasCompatibleType; | |
| Types(Predicate<Object> hasCompatibleType) { | |
| this.hasCompatibleType = hasCompatibleType; | |
| } | |
| } | |
| ``` | |
| This has several advantages on top of sidestepping this checker, e.g. not tying | |
| you to a particular functional interface type -- your callers should e.g. use | |
| `STRING::hasCompatibleType` instead of `STRING.hasCompatibleType` which only | |
| works for one interface type. |