为什么枚举实现不能访问枚举类中的私有字段?

28

我刚刚回答了这个问题,解释了如何解决编译问题:

如何通过覆盖方法在Java枚举中使用字段?

但是我不明白为什么首先会出现错误。

以下是一个以枚举形式书写的示例:

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();
}
enum实现中,FIRST无法访问someField,但在抽象类中可以访问。此外,添加super可以解决该问题,去掉字段上的private修饰符也可以。有人知道为什么会出现这种行为上的细微差异吗?

使用this.someField代替someField,对于枚举/类情况下都会生成相同的错误信息:The field Main.MyClass.someField is not visible - sp00m
在第一种情况下,您在创建对象时访问它,在第二种情况下作为子类,对吗? - flotothemoon
无法对非静态字段进行静态引用。 - Wundwin Born
4个回答

18

你的抽象类与枚举不相同,因为枚举隐式地具有 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中 "Static Nested Classes" 章节所解释的那样:

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

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


谢谢@sp00m,我在发布问题之前确实快速查看了枚举规范,但是错过了与静态内部类和变量访问的交互。 - Tim B
枚举类型只有在它的枚举常量没有自己的类体时才隐式地被声明为 final,正如JLS 8.9中所述。除了为枚举常量创建的子类之外,您仍然无法扩展非 final 的枚举类型。 - user2357112

4

当标识符被解析时,Java会优先选择词法作用域而非继承成员。因此,当您有一个内部类扩展外部类并且在不使用thissuper的情况下使用外部类的字段时,将访问外部实例的字段,如果内部类为static则会失败,因为此时没有外部实例。相反,在使用super时,您正在明确访问继承的成员。请注意,enum类隐式地是static的。您甚至可以使用this来访问继承的成员,但如果它声明为private,则必须使用((MyClass)this).someField来访问它。


0

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

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


0

我不同意被接受的答案。

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

来自JSL第8章.类

枚举常量的可选类体隐式定义了一个匿名类声明(§15.9.5),该匿名类扩展了直接封闭的枚举类型。类体受匿名类的通常规则所控制。

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

来自JSL第15章:

Java编译器会根据类实例创建表达式自动生成匿名类声明。匿名类从不是抽象的(§8.1.1.1)。匿名类始终是隐式final的(§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;
        }
    }
}

更多:


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