Почему реализация 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
в поле.
Кто-нибудь знает, почему эта небольшая причуда в поведении происходит?
Ответы
Ответ 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;
}
}
}
Подробнее: