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

JavaEnums

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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 - Java

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 - Java

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.

Solution 4 - Java

  1. "Nested enum types are implicitly static." 8.9 Enums

  2. It is reasonable to infer that nested enum types implicitly contain the static access modifier.

  3. "It is a compile-time error if a local class declaration contains any one of the following access modifiers: public, protected, private, or static."14.3 14.3 Local Class Declarations

Solution 5 - Java

It's weird because the java inner class definition says that compile-time constants can be declared static, and a member of a Enum is clearly compile-time constant, plus enum is a static class, suposedly...

Documentation:

8.1.3 Inner Classes and Enclosing Instances

(...) Inner classes may not declare static members, unless they are compile-time constant fields.

class Outer{
	class Inner extends HasStatic{
		static final int x = 3;			// ok - compile-time constant
		static int y = 4; 			// compile-time error, an inner class
	}
	static class NestedButNotInner{
		static int z = 5; 			// ok, not an inner class
	}
	interface NeverInner{}				// interfaces are never inner
}

Solution 6 - Java

http://mindprod.com/jgloss/enum.html gives a good description of java enums - as previously mentioned, enums are defined as static so they can't be declared as locals

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionbruno condeView Question on Stackoverflow
Solution 1 - JavakdgregoryView Answer on Stackoverflow
Solution 2 - JavaBasil BourqueView Answer on Stackoverflow
Solution 3 - JavaJon SkeetView Answer on Stackoverflow
Solution 4 - JavaemoryView Answer on Stackoverflow
Solution 5 - Javano_ripcordView Answer on Stackoverflow
Solution 6 - JavaPeterView Answer on Stackoverflow