This document proposes changes to the Java Language Specification to support enhancements to the enum language feature. See JEP 301 for an overview.

8.9 Enum Classes

An enum declaration specifies a new enum class, a special kind of class type.

In this and other sections, it's preferable to use the term enum class rather than enum type, especially now that there is no longer a one-to-one relationship between enum declarations and actual enum types (i.e., class types, possibly parameterized or raw, that name an enum). That said, there are a few places where JLS uses "enum type" as shorthand for "enum type declaration", in parallel with "class type", "interface type", and "annotation type" (see, e.g., 6.1 and 7.5.1). We don't necessarily propose changing all of those sections.

EnumDeclaration:

{ ClassModifier } enum Identifier [ TypeParameters ]

[ Superinterfaces ] EnumBody

It is a compile-time error if an enum declaration has the modifier abstract or final.

An enum declaration is implicitly final unless it contains at least one enum constant that has a class body (8.9.1).

A nested enum class is implicitly static. It is permitted for the declaration of a nested enum class to redundantly specify the static modifier.

This implies that it is impossible to declare an enum class in the body of an inner class (8.1.3), because an inner class cannot have static members except for constant variables.

It is a compile-time error if the same keyword appears more than once as a modifier for an enum declaration.

An enum class may be generic (8.1.2) and may declare superinterfaces (8.1.5).

The direct superclass of a non-generic enum class E is Enum<E>; the direct superclass of a generic enum class E<P1,...,Pn> is Enum<E<P1,...,Pn>> (8.1.4).

This rule adds a compatibility risk for developers who add type parameters to existing enums—their refactored enum will not work with client code that wants to operate on Enum<E>. However, most clients don't interact with enums through their Enum superclass type; when the type is used, it's usually with a wildcard.

An enum class has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum class (15.9.1).

In addition to the compile-time error, three further mechanisms ensure that no instances of an enum class exist beyond those defined by its enum constants:

8.9.1 Enum Constants

The body of an enum declaration may contain enum constants. An enum constant defines an instance of the enum class.

EnumBody:

{ [ EnumConstantList ] [ , ] [ EnumBodyDeclarations ] }

EnumConstantList:

EnumConstant { , EnumConstant }

EnumConstant:

{ EnumConstantModifier } Identifier [ TypeArguments ]

[ ( [ ArgumentList ] ) ] [ ClassBody ]

EnumConstantModifier:

Annotation

The following productions from 4.5.1 and 15.12 are shown here for convenience:

TypeArguments:

< TypeArgumentList >

TypeArgumentList:

TypeArgument { , TypeArgument }

ArgumentList:

Expression { , Expression }

The rules for annotation modifiers on an enum constant declaration are specified in 9.7.4 and 9.7.5.

The Identifier in an EnumConstant may be used in a name to refer to the enum constant.

The scope and shadowing of an enum constant is specified in 6.3 and 6.4.

If an enum class is generic, each enum constant must provide type arguments immediately after the Identifier, or a compile-time error occurs.

We could relax this restriction to allow raw enum constants (that is, enum constants that are instances of the raw enum type rather than a parameterization). But there is no need for such flexibility from a maintenance perspective, since the enum and its constants are declared in the same source file.

It is a compile-time error if any type argument provided by an enum constant is a wildcard (4.5.1).

Where an enum constant of an enum class E provides type arguments T1, ..., Tn, it is a compile time error if the type E<T1,...,Tn> is not well-formed (4.5).

An enum constant may be followed by arguments, which are passed to the constructor of the enum when the constant is created during class initialization as described later in this section. The constructor to be invoked is chosen using the normal rules of overload resolution (15.12.2). If the arguments are omitted, an empty argument list is assumed.

The optional class body of an enum constant implicitly defines an anonymous class declaration (15.9.5) that extends the immediately enclosing enum class, parameterized by the given type arguments, if any. The class body is governed by the usual rules of anonymous classes; in particular it cannot contain any constructors. Instance methods declared in these class bodies may be invoked outside the enclosing enum type only if they override accessible methods in the enclosing enum type (8.4.8).

The anonymous class type implicitly defined by an enum constant of an enum class E is referred to as an enum constant type of E.

Compilation of anonymous classes representing enum constant types requires special treatment, because they can be accessed from separately-compiled code. Per 13.1, the class name is standardized. It is also necessary to set the class accessibility such that any client who can reference the enum constant can also access the class.

It is a compile-time error for the class body of an enum constant to declare an abstract method.

Suggest deleting this sentence because it's redundant: "the class body is governed by the usual rules of anonymous classes".

It is a compile-time error for an enum constant with a class body to declare a finalizer (12.6). An instance of an enum class may never be finalized.

It is a compile-time error to reference a static field of an enum class from an instance initializer or instance variable initializer expression of an enum constant's class body, unless the field is a constant variable (4.12.4).

This is a bug fix. These two rules are asserted for enum class bodies (8.9.2), but were not asserted for enum constant bodies. javac already enforces them.

The arguments and class body of an enum constant appear in a static context (8.1.3). As a result, references to the type parameters and members of the enum class, and uses of the this and super keywords, are restricted (6.5.6.1, 8.1.2, 15.8.3, 15.12.3).

Because there is only one instance of each enum constant, it is permitted to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant.

The equals method in Enum is a final method that merely invokes super.equals on its argument and returns the result, thus performing an identity comparison.

The first paragraph in the above note is presented as normative in JLS 8, but it's not. In particular, "permitted" is not meant to suggest any sort of error condition.

8.9.2 Enum Body Declarations

In addition to enum constants, the body of an enum declaration may contain constructor and member declarations as well as instance and static initializers.

EnumBodyDeclarations:

; { ClassBodyDeclaration }

The following productions from 8.1.6 are shown here for convenience:

ClassBodyDeclaration:

ClassMemberDeclaration
InstanceInitializer
StaticInitializer
ConstructorDeclaration

ClassMemberDeclaration:

FieldDeclaration
MethodDeclaration
ClassDeclaration
InterfaceDeclaration
;

Any constructor or member declarations in the body of an enum declaration apply to the enum class exactly as if they had been present in the body of a normal class declaration, unless explicitly stated otherwise.

It is a compile-time error if a constructor declaration in an enum declaration is public or protected (6.6).

It is a compile-time error if a constructor declaration in an enum declaration contains a superclass constructor invocation statement (8.8.7.1).

It is a compile-time error to reference a static field of an enum class from constructors, instance initializers, or instance variable initializer expressions of the enum class, unless the field is a constant variable (4.12.4).

In an enum declaration, a constructor declaration with no access modifiers is private.

In an enum declaration with no constructor declarations, a default constructor is implicitly declared. The default constructor is private, has no formal parameters, and has no throws clause.

In practice, a compiler is likely to mirror the Enum class by declaring String and int parameters in the default constructor of an enum class. However, these parameters are not specified as "implicitly declared" because different compilers do not need to agree on the form of the default constructor. Only the compiler of an enum class knows how to instantiate the enum constants; other compilers can simply rely on the implicitly declared public static fields of the enum class (8.9.3) without regard for how those fields were initialized.

It is a compile-time error if an enum declaration E has an abstract method m as a member, unless E has at least one enum constant and all of E's enum constants have class bodies that provide concrete implementations of m.

It is a compile-time error for an enum declaration to declare a finalizer (12.6). An instance of an enum class may never be finalized.

...

8.9.3 Enum Members

The members of an enum class E are all of the following:

/**
* Returns an array containing the constants of this enum
* **class**, in the order they're declared.  This method may be
* used to iterate over the constants as follows:
*
*    for(E c : E.values())
*        System.out.println(c);
*
* @return an array containing the constants of this enum
* **class**, in the order they're declared
*/
public static E[] values();

/**
* Returns the enum constant of this **class** with the specified
* name.
* The string must match exactly an identifier used to declare
* an enum constant in this **class**.  (Extraneous whitespace
* characters are not permitted.)
*
* @return the enum constant with the specified name
* @throws IllegalArgumentException if this enum **class** has no
* constant with the specified name
*/
public static E valueOf(String name);

It follows that the declaration of enum class E cannot contain fields that conflict with the implicitly declared fields corresponding to E's enum constants, nor contain methods that conflict with implicitly declared methods or override final methods of class Enum.

...

6.3 Scope of a Declaration

...

The scope of an enum constant C declared in an enum class E is the body of E, and any case label of a switch statement whose expression is of enum type T, such that T is E, a parameterization of E, or an enum constant type of E (14.11).

...

8.1.2 Generic Classes and Type Parameters

...

It is a compile-time error to refer to a type parameter of a generic class C in any of the following:

...

8.1.3 Inner Classes and Enclosing Instances

...

A statement or expression occurs in a static context if and only if the innermost method, constructor, instance initializer, static initializer, field initializer, enum constant, or explicit constructor invocation statement enclosing the statement or expression is a static method, a static initializer, the variable initializer of a static variable, an enum constant, or an explicit constructor invocation statement (8.8.7.1).

This is a bug fix: it was not obvious in JLS 8 that references to, say, instance methods, are illegal as arguments to enum constants.

...

13.1 The Form of a Binary

Programs must be compiled either into the class file format specified by The Java Virtual Machine Specification, Java SE 8 Edition, or into a representation that can be mapped into that format by a class loader written in the Java programming language. The resulting class file must have certain properties. A number of these properties are specifically chosen to support source code transformations that preserve binary compatibility. The required properties are:

  1. The class or interface must be named by its binary name, which must meet the following constraints:

...

  1. Given a legal expression denoting a field access in a class C, referencing a field named f that is not a constant variable and is declared in a (possibly distinct) class or interface D, we define the qualifying type of the field reference as follows:

    ...

    The reference to f must be compiled into a symbolic reference to the erasure (4.6) of the qualifying type of the reference, plus the simple name of the field, f. The reference must also include a symbolic reference to the erasure of the declared type of the field binary field type so that the verifier can check that the type is as expected:

    Adding a class body to an enum constant (or recompiling an enum with a new source version) shouldn't break binary compatibility. To support this, the compiled type of an enum constant field is always the enum class, not an anonymous enum constant class.

    The term binary field type abstractly describes the JVM field descriptor (JVMS 4.3.2). We introduce the term here as a way to describe the mapping from a field's Java type to its JVM descriptor, now that this mapping must be special-cased for enum constants.

    While we're at it, we might as well use binary method type, below, to abstractly describe the JVM method descriptor (JVMS 4.3.3).

  2. Given a method invocation expression or a method reference expression in a class or interface C, referencing a method named m declared (or implicitly declared (9.2)) in a (possibly distinct) class or interface D, we define the qualifying type of the method invocation as follows:

    ...

    A reference to a method must be resolved at compile time to a symbolic reference to the erasure (4.6) of the qualifying type of the invocation, plus the erasure of the signature (8.4.2) of the method. The signature of a method must include the simple name of the method, m, and the binary method type, which consists of all of the following as determined by 15.12.3:

    A reference to a method must also include either a symbolic reference to the erasure of the return type of the denoted method or an indication that the denoted method is declared void and does not return a value.

...

A binary representation for a class or interface must also contain all of the following:

...

  1. A specification of each field declared in the class or interface, given as the simple name of the field and a symbolic reference to the erasure of the type of the field binary field type, as described above.

  2. If it is a class, then the erased signature of each constructor, as described above.

  3. For each method declared in the class or interface (excluding, for an interface, its implicitly declared methods (9.2)), its erased signature and return type, as described above simple name and binary method type, as described above.

...

  1. A construct emitted by a Java compiler must be marked as mandated if it corresponds to a formal parameter declared implicitly in source code (8.8.1, 8.8.9, 8.9.3, 15.9.5.1).

The following formal parameters are declared implicitly in source code:

For reference, the following constructs are declared implicitly in source code, but are not marked as mandated because only formal parameters can be so marked in a class file (JVMS 4.7.22):

...

13.4.26 Evolution of Enums

Adding or reordering constants in an enum will not break compatibility with pre-existing binaries.

Adding a class body to an enum constant declaration will not break compatibility with pre-existing binaries.

If a pre-existing binary attempts to access an enum constant that no longer exists, the client will fail at run time with a NoSuchFieldError. Therefore such a change is not recommended for widely distributed enums.

Similarly, removing a class body from an enum constant deletes an implicitly-defined class that might be referenced from other compilation units, leading to linkage errors.

In all other respects, the binary compatibility rules for enums are identical to those for classes.

14.11 The switch Statement

...

The type of the Expression must be char, byte, short, int, Character, Byte, Short, Integer, String, or an enum class type (8.9), or a subtype of one of these, or a compile-time error occurs.

The switch type of a switch statement is defined as follows:

...

Given a switch statement, all of the following must be true or a compile-time error occurs:

...

The changes here address an outstanding bug (JDK-6404665): the type of Expression may be a type variable (or intersection?) bounded by a supported class type, rather than the class type itself. JEP 301 exacerbates this situation:

The goal is to allow any case constants that are instances of the appropriate class or enum.

Alternatively, we could agressively prohibit case constants that belong to the appropriate class, but that the type system can prove are incompatible with the Expression (assuming no heap pollution). But this would be a source-incompatible change, and the analysis would be complex.

15.9.1 Determining the Class to Be Instantiated

If the class instance creation expression ends in a class body, then the class being instantiated is an anonymous class. Then:

If a class instance creation expression does not declare an anonymous class, then:

15.13 Method Reference Expressions

...

If a method reference expression has the form ClassType :: [ TypeArguments ] new, then:

...