Why can't enums be declared locally in a method?

Today, I found myself coding something like this ...

public class LocalEnums {

    public LocalEnums() {
    }

    public void foo() {
        enum LocalEnum {
            A,B,C
        };

        // ....
        // class LocalClass { }

    }
}

and I was kind of surprised when the compiler reported an error on the local enum:

The member enum LocalEnum cannot be local

Why can't enums be declared local like classes?

I found this very useful in certain situations. In the case I was working, the rest of the code didn't need to know anything about the enum.

Is there any structural/design conflict that explains why this is not possible or could this be a future feature of Java?


Solution 1:

Enums are static nested classes because they define static member variables (the enum values), and this is disallowed for inner classes: http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.1.3

Update: I was looking through the JLS (java language specification) for more detail on the restrictions of static nested classes, and didn't find it (although it's probably there, hidden under a different topic). From a pure implementation perspective, there's no reason that this couldn't be done. So I suspect that it was a language philosophy issue: it shouldn't be done, therefore won't be supported. But I wasn't there, so that's pure speculation.

As a comment: if your methods are large enough that they require their own enums, then it's a strong sign that you need refactoring.

Solution 2:

Other Answers are outmoded as of Java 16 (released 2021-03) and Java 17 (released 2021-09).

Local enums in Java 16+

As part of the records feature introduced in Java 16, enums may now be defined locally. Indeed, records, enums, and interfaces can all be local now.

To quote JEP 395:

The ability to declare local record classes, local enum classes, and local interfaces was introduced.

The addition of local record classes is an opportunity to add other kinds of implicitly-static local declarations.

Nested enum classes and nested interfaces are already implicitly static, so for consistency we define local enum classes and local interfaces, which are also implicitly static.

You can now run the following example code to use an enum defined within a method.

private void demoLocalEnum ( )
{
    enum Color { PURPLE, SAFETY_ORANGE }
    System.out.println( Color.PURPLE );
}

In this screenshot, we can see how the local enum exists only within its defining method. A sibling method cannot see that enum. Trying to call that enum from another method generates an error.

Screenshot of an error caused by a sibling method trying to call a local enum defined in some other method.

See a demo of such code running in the IntelliJ 2020.2 IDE.

Implicitly static

There is one limitation with a locally defined enum: No access to state in surrounding class.

Example code:

private void demoLocalEnum ()
{
    int x = 42;

    enum Color
    {
        PURPLE,
        SAFETY_ORANGE;

        public void demo ()
        {
            System.out.println( "Now is " + Instant.now() );  // This line works.
            System.out.println( "x = " + x );  // ERROR — Non-static variable 'x' cannot be referenced from a static context.
        }
    }

    Color.PURPLE.demo();
}

My IntelliJ IDE reports error:

Non-static variable 'x' cannot be referenced from a static context

As mentioned in the JEP 395 quote above, a local enum is implicitly static. So any methods you may define on your local enum cannot access state on the surrounding outer class.

To quote JEP 395, regarding local records but applying to local enums as well:

Local record classes are a particular case of nested record classes. Like nested record classes, local record classes are implicitly static. This means that their own methods cannot access any variables of the enclosing method; in turn, this avoids capturing an immediately enclosing instance which would silently add state to the record class. The fact that local record classes are implicitly static is in contrast to local classes, which are not implicitly static. In fact, local classes are never static — implicitly or explicitly — and can always access variables in the enclosing method.

Solution 3:

I rarely find myself writing any types within a method, unless it's an anonymous inner class. You can, however, write nested enums:

public class NestedEnum
{
    private enum MyEnum
    {
        X, Y, Z
    }

    public void foo()
    {
    }
}

I don't think I'd really want to read a method which declared a new type within it - do you have any concrete reason for wanting to declare it inside the method instead of just as a nested type? I can see the "no other methods need to know" argument, but I think a comment can sort that out and still leave more readable code.