Подтвердить что ты не робот

Почему реализация enum не может получить доступ к закрытым полям в классе enum

Я просто ответил на этот вопрос, сказав, как решить проблему компиляции:

Как использовать поля в перечислении 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();
}

В случае FIRST в реализации enum он не может получить доступ к someField. Однако в случае абстрактного класса он может.

Кроме того, добавление super устраняет проблему, как и удаление модификатора private в поле.

Кто-нибудь знает, почему эта небольшая причуда в поведении происходит?

4b9b3361

Ответ 1

Ваш абстрактный класс не эквивалентен вашему перечислению, поскольку перечисления являются неявно публичными статическими окончательными. Таким образом, вы будете наблюдать такое же поведение, если используете:

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. Вы также можете использовать this, если поле было protected, а не private.

Ответ 2

Когда идентификатор разрешен, Java предпочитает лексическую область над унаследованными членами. Поэтому, когда у вас есть внутренний класс, который расширяет внешний класс и использует поле внешнего класса без использования this или super, к нему обращается поле внешнего экземпляра, который терпит неудачу, если внутренний класс static, как там тогда нет внешнего экземпляра. Напротив, при использовании super вы явно обращаетесь к унаследованному члену. Обратите внимание, что классы enum неявно static. Вы можете даже использовать this для доступа к унаследованному члену, но вы должны использовать ((MyClass)this).someField для доступа к нему, если его объявили private.

Ответ 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.

В другом случае ваши константы enum ведут себя как статические вложенные подкласс, а не внутренние классы, поэтому они не имеют ссылки на внешний класс, а только на их суперкласс.

Ответ 4

Я не согласен с принятым ответом.

Объявление enum const неявно public static final, но не класс, к которому принадлежит перечисление const.

Из JSL Chapter 8.Classes

Необязательное тело класса константы enum неявно определяет анонимное объявление класса (§15.9.5), которое расширяет сразу включающий тип перечисления. Тело класса определяется обычными правилами анонимных классов.

А какие "правила анонимных классов"?

От JSL Chapter 15:

Объявление анонимного класса автоматически выводится из выражения создания экземпляра класса компилятором Java.

Анонимный класс не является абстрактным (§8.1.1.1).

Анонимный класс всегда неявно окончателен (§8.1.1.2).

Анонимный класс всегда является внутренним классом (§8.1.3); он никогда не статичен (§8.1.1, §8.5.1).

И если эквивалентный класс enum является статическим классом, как объяснить следующую ошибку?

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

Но почему внутренний класс не может получить доступ к внешнему полю класса?

Возможный эквивалентный класс enum может выглядеть следующим образом, что дает ту же ошибку, что и класс перечисления:

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

Подробнее: