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

6

66

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?

Epact answered 31/3, 2009 at 13:2 Comment(1)
Currently the highest voted answer still claims it's impossible, even though with Java 16 this has changed. If so, please skip down to the next one to see what changed in Java 16. (I hope that votes will eventually make this comment wrong and unnecessary).Holily
P
24

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.

Purpurin answered 9/7, 2020 at 4:44 Comment(0)
M
37

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.

Mic answered 31/3, 2009 at 13:5 Comment(5)
kdgregory, my method don't have to be that large to use the power of enums... Enums can simplify/clarify your code. So refactoring may not be an issue here.Epact
Philosophy is no reason to exclude a feature. Lack of usefulness, likelihood of leading to dangerous code (surprising or undefined behaviour) and technical difficulty of inclusion are valid reasons. But I don't see why any of those apply here.Emarginate
@Emarginate - Language philosophy is everything: what to include, what to exclude, and how the language looks. If you don't have a philosophy, you get PHP.Mic
@Emarginate Philosophy is what makes us believe that a feature is not useful, that it may lead to dangerous code, etc. This can't be evaluated objectively and Java is a tiny bit off, sometimes. +1 to kdgregory for the PHP comment.Emcee
Sorry for the downvote: this answer isn't wrong (or at least wasn't when it was written), but with Java 16 it's no longer up-to-date.Holily
P
24

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.

Purpurin answered 9/7, 2020 at 4:44 Comment(0)
Z
16

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.

Zodiac answered 31/3, 2009 at 13:6 Comment(1)
Jon, I don't often write local classes as well. But, In C# you can have something like var v = new {Prop = "Hello!!!"}; and that can be useful in certain situations. You can approximate this behavior with a local class in Java. So, if classes, why not enums?Epact
A
11
  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
Affright answered 26/5, 2010 at 5:23 Comment(2)
Private nested enums (as an enum declaration within a method probably should be) can be inlined as constants at compile time. In fact, it's possible to write the equivalent final int X = .... So whatever logic is there is broken.Emarginate
@Emarginate I 100% agree. We should be able to put enums and interfaces inside methods. My answer merely explained why local enums are forbidden by policy. I definitely don't agree with the policy.Affright
P
3

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
}
Protomorphic answered 1/4, 2009 at 4:41 Comment(0)
E
2

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

Ezar answered 31/3, 2009 at 13:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.