【问题标题】:Why can't enums be declared locally in a method?为什么不能在方法中本地声明枚举?
【发布时间】:2009-03-31 13:02:28
【问题描述】:

今天,我发现自己在编写类似这样的代码......

public class LocalEnums {

    public LocalEnums() {
    }

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

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

    }
}

当编译器在本地enum报错时,我有点惊讶:

成员枚举 LocalEnum 不能 本地

为什么枚举不能像一样声明为本地的?

我发现这在某些情况下非常有用。在我工作的情况下,其余代码不需要知道任何关于enum 的信息。

是否存在任何结构/设计冲突来解释为什么这是不可能的,或者这可能是 Java 的未来特性吗?

【问题讨论】:

    标签: java enums


    【解决方案1】:

    枚举是静态嵌套类,因为它们定义静态成员变量(枚举值)内部类不允许这样做:@ 987654321@

    更新:我正在查看 JLSjava 语言规范)以了解有关静态嵌套类限制的更多详细信息,但没有找到(虽然它可能在那里,隐藏在不同的主题下)。从纯粹的实现角度来看,没有理由不能做到这一点。所以我怀疑这是一个语言哲学问题:不应该这样做,因此不会得到支持。但我不在场,所以这纯粹是猜测。

    作为评论:如果您的方法足够大以至于它们需要自己的枚举,那么这是您需要重构的强烈迹象。

    【讨论】:

    • kdgregory,我的方法不必那么大就可以使用枚举的power...枚举可以简化/澄清你的代码。所以重构在这里可能不是问题。
    • 哲学没有理由排除一个特性。缺乏实用性、导致危险代码的可能性(令人惊讶或未定义的行为)和包含的技术困难是正当的理由。但我不明白为什么这些都适用于此。
    • @dhardy - 语言哲学是一切:包含什么、排除什么以及语言的外观。如果你没有哲学,你会得到 PHP。
    • @dhardy 哲学让我们相信一个特性没有用,它可能导致危险代码等。这无法客观地评估,有时 Java 有点偏离。 +1 到 kdgregory 的 PHP 评论。
    【解决方案2】:

    自 Java 16(2021-03 发布)和 Java 17(2021-09 发布)起,其他答案已过时。

    Java 16+ 中的本地枚举

    作为Java 16 中引入的records 功能的一部分,现在可以在本地定义枚举。事实上,记录、枚举和接口现在都可以是本地的了。

    引用 JEP 395:

    引入了声明本地记录类、本地枚举类和本地接口的能力。

    添加本地记录类是添加其他类型的隐式静态本地声明的机会。

    嵌套枚举类和嵌套接口已经是隐式静态的,因此为了保持一致性,我们定义了本地枚举类和本地接口,它们也是隐式静态的。

    您现在可以运行以下示例代码来使用在方法中定义的枚举。

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

    在此屏幕截图中,我们可以看到本地枚举如何仅存在于其定义方法中。兄弟方法看不到该枚举。尝试从另一个方法调用该枚举会产生错误。

    IntelliJ 2020.2 IDE 中查看demo of such code running

    隐式static

    本地定义的枚举有一个限制:无法访问周围类中的状态。

    示例代码:

    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();
    }
    

    我的IntelliJIDE报错:

    不能从静态上下文中引用非静态变量“x”

    正如上面引用的 JEP 395 中提到的,本地枚举隐含地是 static。因此,您可以在本地枚举上定义的任何方法都无法访问周围外部类的状态。

    引用 JEP 395,关于本地记录,但也适用于本地枚举:

    本地记录类是嵌套记录类的一个特例。与嵌套记录类一样,本地记录类是隐式静态的。这意味着他们自己的方法不能访问封闭方法的任何变量;反过来,这避免了捕获立即封闭的实例,该实例会默默地将状态添加到记录类。本地记录类是隐式静态的这一事实与本地类相反,后者不是隐式静态的。事实上,局部类永远不是静态的——隐式或显式——并且总是可以访问封闭方法中的变量。

    【讨论】:

      【解决方案3】:

      我很少发现自己在方法中编写任何类型,除非它是一个匿名内部类。但是,您可以编写嵌套枚举:

      public class NestedEnum
      {
          private enum MyEnum
          {
              X, Y, Z
          }
      
          public void foo()
          {
          }
      }
      

      我不认为我真的想读取一个在其中声明新类型的方法 - 你有什么具体的理由想要在方法中声明它而不是仅仅嵌套类型?我可以看到“不需要知道其他方法”的论点,但我认为注释可以解决这个问题,并且仍然留下更易读的代码。

      【讨论】:

      • Jon,我也不经常写本地类。但是,在 C# 中,您可以使用类似 var v = new {Prop = "Hello!!!"};这在某些情况下很有用。您可以使用 Java 中的本地类近似此行为。那么,如果是类,为什么不使用枚举呢?
      【解决方案4】:
      1. “嵌套枚举类型是隐式的 静态的。”8.9 Enums

      2. 可以合理推断 嵌套枚举类型隐式包含 静态访问修饰符。

      3. "如果一个 本地类声明包含任何 以下访问权限之一 修饰符:公共的,受保护的, 私有的或静态的。”14.3 14.3 Local Class Declarations

      【讨论】:

      • 私有嵌套枚举(可能应该是方法中的枚举声明)可以在编译时作为常量内联。事实上,可以写成等价的final int X = ...。所以不管有什么逻辑都被打破了。
      • @dhardy 我 100% 同意。我们应该能够将枚举和接口放入方法中。我的回答只是解释了为什么政策禁止本地枚举。我绝对不同意这项政策。
      【解决方案5】:

      这很奇怪,因为java内部类定义说编译时常量可以声明为静态,而Enum的成员显然是编译时常量,加上enum是静态类,据说......

      Documentation:

      8.1.3 内部类和封闭实例

      (...) 内部类不能声明静态成员,除非它们是编译时常量字段。

      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
      }
      

      【讨论】:

        【解决方案6】:

        http://mindprod.com/jgloss/enum.html 很好地描述了 java 枚举 - 如前所述,枚举被定义为静态,因此它们不能被声明为局部变量

        【讨论】:

          猜你喜欢
          • 2022-11-28
          • 1970-01-01
          • 1970-01-01
          • 2014-08-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-30
          相关资源
          最近更新 更多