【问题标题】:Why can enum implementations not access private fields in the enum class为什么枚举实现不能访问枚举类中的私有字段
【发布时间】:2014-07-29 08:25:09
【问题描述】:

我只是通过说如何解决编译问题来回答这个问题:

How to use fields in java enum by overriding the method?

但我不明白为什么首先会发生错误。

下面是写成枚举的例子:

public enum MyEnum {


    FIRST {
        @Override
        public String doIt() {
            return "1: " + someField; //error
        }
    },
    SECOND {
        @Override
        public String doIt() {
            return "2: " + super.someField; //no error
        }
    };

    private String someField;


    public abstract String doIt();

} 

这和抽象类完全一样

abstract class MyClass {
    class FIRST extends MyClass {
        @Override
        public String doIt() {
            return "1: " + someField; //no error
        }
    };
    class SECOND extends MyClass {
        @Override
        public String doIt() {
            return "2: " + super.someField; //no error
        }
    };

    private String someField;

    public abstract String doIt();
}

对于FIRSTenum 实现中的情况,它不能访问someField。但是在抽象类的情况下它可以。

另外添加super 可以解决问题,删除字段上的private 修饰符也是如此。

有谁知道为什么会出现这种轻微的怪癖?

【问题讨论】:

  • 使用 this.someField 而不是 someField 会为两种枚举/类情况生成相同的错误消息:The field Main.MyClass.someField is not visible
  • 那么在第一种情况下,您在创建对象时访问它,在第二种情况下作为子类访问它,对吧?
  • 无法对非静态字段进行静态引用。

标签: java enums


【解决方案1】:

你的抽象类不等同于你的枚举,因为枚举是隐式的 public static final。因此,如果您使用以下代码,您将观察到相同的行为:

abstract class MyClass {

    static class FIRST extends MyClass {

        @Override
        public String doIt() {
            return "1: " + someField; // error
        }

    };

    static class SECOND extends MyClass {

        @Override
        public String doIt() {
            return "2: " + super.someField; // no error
        }

    };

    private String someField;

    public abstract String doIt();

}

正如http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html,“静态嵌套类”一章中所述:

静态嵌套类不能直接引用实例变量或 在其封闭类中定义的方法:它只能通过 对象引用。

因此需要super。如果字段为protected 而不是private,您也可以使用this

【讨论】:

  • 感谢@sp00m,在发布问题之前,我确实快速浏览了枚举规范,但错过了与静态内部类和变量访问的交互。
  • 只有在枚举常量都没有自己的类体时,枚举才是隐式最终的,如JLS 8.9 中所述。但是,除了为枚举常量创建的子类之外,您仍然不能扩展非最终枚举。
【解决方案2】:

当一个标识符被解析时,Java 更喜欢词法范围而不是继承的成员。因此,当您有一个扩展外部类的内部类并使用外部类的字段而不使用thissuper 时,如果内部类为static,则访问外部实例的字段会失败则不是外部实例。相反,当使用super 时,您正在显式访问继承的成员。请注意,enum 类隐含为static。您甚至可以使用this 访问继承的成员,但如果声明为private,则必须使用((MyClass)this).someField 访问它。

【讨论】:

    【解决方案3】:

    FIRST 类是MyClass 的内部类,也是一个子类。访问其中的someField 时没有看到错误的原因是因为您访问的是外部类的someField,而不是超类。

    class MyClass {
        class FIRST extends MyClass {
            @Override
            public String doIt() {
                super.someField = "super";
                return "1: " + someField;
            }
        };
    
        private String someField = "outer";
    
        public String doIt(){return "";}
    
        public static void main(String[] args) {
            System.out.println(new MyClass().new FIRST().doIt());
        }
    }
    

    打印1: outer

    在另一种情况下,您的枚举常量表现为静态嵌套子类,而不是内部类,因此它们没有对外部类的引用,只有它们的超类。

    【讨论】:

      【解决方案4】:

      我不同意接受的答案。

      枚举常量声明是隐含的public static final,但不是枚举常量所属的类。

      来自JSL Chapter 8.Classes

      枚举常量的可选类主体隐式定义了一个匿名类声明(第 15.9.5 节),它扩展了直接封闭的枚举类型。类主体受匿名类的常规规则管理。

      “匿名类的规则”是什么?

      来自JSL Chapter 15:

      匿名类声明由 Java 编译器自动从类实例创建表达式派生。

      匿名类永远不是抽象的(第 8.1.1.1 节)。

      匿名类总是隐式最终的(第 8.1.1.2 节)。

      匿名类始终是内部类(第 8.1.3 节);它永远不是静态的(§8.1.1、§8.5.1)。

      如果枚举等价类是静态类,下面的错误怎么解释?

      public enum MyClass {
          First {
              public static int b;  //(2)Illegal static declaration in inner class
          };
      }
      

      但是为什么内部类不能访问外部类的字段呢?

      一个可能的枚举等效类可能如下所示,它给出与枚举类相同的错误:

      abstract class MyClass {    
          private int someField;
          static {
              class First extends MyClass {
                  public void method() {
                      System.out.println(someField);
                  }
                  private static int b;
              }
          }
      }
      

      更多:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-22
        • 2020-02-16
        • 2010-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多