为什么在Java中无法在内部类中创建枚举?

75

我想要做的是这样的:

public class History {
    public class State {
        public enum StateType {

StateType 枚举必须定义在静态成员类型内部。这是 Eclipse 给出的编译错误信息。

当我将 State 类设置为静态时,该错误消失了。虽然我可以将 State 设置为静态,但我不明白为什么不能在内部类中声明 enum


1
请看这个链接:https://dev59.com/3XRB5IYBdhLWcg3wJklC - s_bei
1
除非你想让State访问History中的方法,否则将State设置为静态类 - ArneHugo
4个回答

101

Java 16+允许这样做

由于JEP 395放宽了内部类中某些静态类型的规则,从Java 16开始(以及后来的LTS版本Java 17),这种做法不再被禁止

§8.1.3. Inner Classes and Enclosing Instances中的新措辞为(重点在于“inner class may declare”):

所有适用于嵌套类的规则也适用于内部类。特别地,内部类可以声明和继承静态成员(§8.2),并声明static初始化器(§8.7),即使内部类本身不是static

这个答案详细解释了这个变化

Java 16之前是被禁止的:

枚举类型如果被定义为嵌套类型,它们总是隐式地static(详见JLS §8.9. Enums):

嵌套的枚举类型是隐式的static

在非静态的嵌套类型(也称为“内部类”,详见JLS §8.1.3. Inner Classes and Enclosing Instances)中,你不能有一个静态嵌套类型:

如果内部类声明了一个显式或隐式的静态成员,除非该成员是常量变量,否则就会产生编译时错误

因此,在非静态的嵌套类型中无法有enum内部类型。


嵌套的 enum 类型为什么隐式地是 static 的? - mrbela
1
如果允许非静态嵌套的枚举类型,那么枚举值应该引用哪个封闭类实例呢?这个想法与枚举类型的基本原则不兼容(即有限的、明确定义的实例集)。 - Joachim Sauer
那么正确的替代方案是什么?我想要一个与我的内部类相关联的“枚举”。 - Daniel Chin
1
@DanielChin:我刚刚编辑了我的答案,添加了细节,从Java 16开始(更重要的是Java 17 LTS版本),这实际上是允许的。因此,另一种选择是升级到Java 17。如果您无法升级,则只需将枚举“放在”内部类旁边(即使它成为兄弟而不是子代)。 - Joachim Sauer

19

如果您像这样声明了一个枚举:

enum Suit {SPADES, HEARTS, CLUBS, DIAMONDS}

Java编译器将为您合成生成以下类:

final class Suit extends java.lang.Enum<Suit> {
  public static final Suit SPADES;
  public static final Suit HEARTS;
  public static final Suit CLUBS;
  public static final Suit DIAMONDS;
  private static final Suit[] $VALUES;
  public static Suit[] values();
  public static Suit valueOf(java.lang.String);
  private Suit();
}

这个类没有意图创建除已经定义的静态字段以外的其他实例(你可以从它的私有构造函数推断出来),但最重要的是,正如接受的答案中所提到的,内部类不能有静态成员 (JLS §8.1.3. 内部类和封闭实例),而枚举合成类却有静态成员,因此它不适合作为内部类。


难道不应该是final static class Suit extends java.lang.Enum<Suit>而不是final class Suit extends java.lang.Enum<Suit>吗?因为枚举有静态变量(例如SPADES, CLUBS等),而内部(嵌套非静态)类不能具有静态声明。 - Marko Cain

6

已经有+Joachim Sauer提供了足够的信息,我只是添加了一些额外的细节。

如果您的内部类是静态嵌套内部类,则可以定义内部枚举。请参见下面的示例:

private static class DbResource {

    public enum DB {
        MERGE_FROM, MERGE_TO, MAIN;
    }
}

2
这是我的使用情况下有效的解决方案:

最初的回答:

public class History {

    public interface HConstants{
         public enum StateType { PAST,CURRENT,FUTURE}
    }

    //Inner class
    public class State implements HConstants{
        public StateType stateField = StateType.PAST;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接