Статический контекст в определении перечисления

Синтаксический сахар, предоставляемый средством Java enum, иногда может быть немного запутанным. Рассмотрим этот пример, который не компилируется:

public enum TestEnum {

    FOO("foo") {
        public void foo() {
            helper();  // <- compiler error
        }
    };

    String name;
    TestEnum(String name) {
        this.name = name;
    }

    public abstract void foo();

    private void helper(){
        // do stuff (using this.name, so must not be static)
    }
}

Может кто-нибудь объяснить, почему компилятор говорит

Нестатический метод 'helper()' не может ссылаться на статический контекст

Как именно этот контекст статичен?

Вы можете сделать эту компиляцию, изменив вызов на this. helper() (здесь есть одна запутанная точка: если мы действительно находимся в "статическом контексте", как предлагает компилятор, как можно "this" работать?) или путем увеличения видимости helper() до уровня по умолчанию. Что бы вы предпочли? Кроме того, не стесняйтесь предлагать лучшее название вопроса: -)

Изменить. Я нашел некоторое обсуждение об этом - но реальных ответов нет. Мой коллега считает, что этот this.helper() работает на самом деле ошибка компилятора. И действительно, с более новыми версиями Java, похоже, не работает (хотя super.helper() делает): "не удается найти символ helper()". (Хотя там что-то странное происходит: после попытки с разными версиями Java я не могу получить this.helper() для компиляции с любым из них...)

Ответ 1

Сообщение об ошибке вводит в заблуждение, просто создайте helper protected и он будет работать.

protected void helper(){
    // can be called from subclasses (such as FOO) since it is not private
}

Ответ 2

Что-то вроде этого описано в книге Java Puzzlers. IIRC, внешний контекст класса всегда рассматривается перед суперклассом. В этом случае найден хелпер. Но мы строим значение в статическом контексте (там эффективно private static final до FOO). Отсюда ошибка.

Попробуйте super.helper();.

Ответ 3

Если я переведю ваш enum в его структуру класса, он будет выглядеть примерно так:

public abstract class TestEnum {

  public static final TestEnum FOO = new FOO("foo") {
    public void foo() {
        helper();  // <- compiler error
    }
 };

  String name;
  TestEnum(String name) {
      this.name = name;
  }

  public abstract void foo();

  private void helper(){
    // do stuff (using this.name, so must not be static)
  }

}

Экземпляр FOO - это любой класс, который расширяет TestEnum. Вот почему я считаю, что вы не можете получить доступ к helper(), потому что он является частным. Поэтому this.helper(), вероятно, не должен работать. Я не уверен, почему работает super.helper(), но, возможно, перечисление предоставляет вам частный доступ к родительскому.

Что касается статической ошибки контекста, я согласен, что сообщение об ошибке не имеет смысла.

Ответ 4

Вы можете рассматривать каждую константу enum как единственный экземпляр подкласса класса enum. Подобно "регулярным" классам, "подклассы" перечисления не могут получить доступ к закрытым членам переименования "суперкласс". (Я не могу воссоздать "это", о котором вы упоминаете.)

Разумным решением является изменить доступ метода от частного к защищенному, чтобы предоставить доступ к подклассам константы перечисления.

Лучшее предложение заголовка вопроса: "Java private enum method?" или просто "Private enum method" (пусть тег Java позаботится о Java-языке)

Ответ 5

Существует article, в котором упоминается эта проблема, к сожалению, она находится на немецком языке. Возможно, вы все равно можете извлечь полезную информацию.

РЕДАКТОРА: Кажется, это совсем другая проблема. Я все еще не понимаю поведение вашего примера, которое я также вижу здесь.