Анонимная реализация Enum не может ссылаться на приватную stati c toString Enum с аргументом - PullRequest
3 голосов
/ 06 февраля 2020

В данном примере кода ...

package example;

public class Main {
    public static void main(String[] args) {
        System.out.println(MyEnum.X.getValue());
    }

    private enum MyEnum {
        X(){
            @Override
            String getValue() {
                return toString("XYZ"); //error here
            }
        };

        abstract String getValue();

        private static String toString(String output) {
            return output;
        }
    }
}

Произошла следующая ошибка компилятора:

Error:(12, 40) java: method toString in class java.lang.Enum<E> cannot be applied to given types;
  required: no arguments
  found: java.lang.String
  reason: actual and formal argument lists differ in length

В IntelliJ возникла другая проблема: toString из toString("XYZ") is подчеркнут красным, и сообщение «toString (java .lang.String)» имеет частный доступ в «example.Main.MyEnum» », с решением« Сделать 'MyEnum.toString' package-private " .

Что странно для меня, так это то, что любое из следующих действий решает эту проблему:

  • Вызов метода через ссылку на перечисление: X.toString("XYZ").
  • Вызов метод через ссылку на класс: MyEnum.toString("XYZ").
  • Вызов метода через super: super.toString("XYZ"). (Но this.toString("XYZ") не работает)
  • Создание метода package-private или publi c
  • Именование метода "toString2"

Теперь, для любого производственного кода я, вероятно, назвал бы метод как-нибудь еще (возможно, что-то более описывающее, что я буду с ним делать) и двинулся бы дальше, но все же мне остается только удивляться, почему это происходит? И почему сообщения об ошибках от IntelliJ и javac отличаются?

Этот вопрос, вероятно, аналогичен Невозможно сделать stati c ссылку на нестатичное c поле memberVariable с закрытой переменной , но я чувствую, что это не полностью объясняет проблему - почему переименование работает?

1 Ответ

4 голосов
/ 06 февраля 2020

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

class Main {
    public static void main(String[] args) {
        Main main = new Main() {
            {
                System.out.println(toString("XYZ")); // same error
            }  
        };
    }

    class Foo {
        {
            System.out.println(toString("XYZ")); // same error
        }
    }

    private static String toString(String output) {
        return output;
    }
}

Объясняется в JLS здесь :

Пример 6.5.7.1-1. Простые имена методов

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

class Super {
    void f2(String s)       {}
    void f3(String s)       {}
    void f3(int i1, int i2) {}
}

class Test {
    void f1(int i) {}
    void f2(int i) {}
    void f3(int i) {}

    void m() {
        new Super() {
            {
                f1(0);  // OK, resolves to Test.f1(int)
                f2(0);  // compile-time error
                f3(0);  // compile-time error
            }
        };
    }
} 

Для вызова f1(0) только один метод с именем f1 находится в объем. Это метод Test.f1(int), объявление которого находится в области видимости всего тела Test, включая объявление анонимного класса. §15.12.1 выбирает поиск в классе Test, поскольку объявление анонимного класса не имеет члена с именем f1. В конце концов, Test.f1(int) разрешается.

Для вызова f2(0) в области действия находятся два метода с именем f2. Во-первых, объявление метода Super.f2(String) находится в области действия объявления анонимного класса. Во-вторых, объявление метода Test.f2(int) находится в области видимости всего тела Test, включая объявление анонимного класса. (Обратите внимание, что ни одно объявление не затеняет другое, потому что в точке, где каждое из них объявлено, другое не входит в область действия.) §15.12.1 выбирает поиск в классе Super, потому что в нем есть член с именем f2. Однако Super.f2(String) не применимо к f2(0), поэтому возникает ошибка времени компиляции. Обратите внимание, что класс Test не ищется.

Для вызова f3(0) три области с именем f3 находятся в области видимости. Во-первых, во-вторых, объявления методов Super.f3(String) и Super.f3(int,int) находятся в области действия объявления анонимного класса. В-третьих, объявление метода Test.f3(int) находится в области видимости всего тела Test, включая объявление анонимного класса. §15.12.1 выбирает поиск в классе Super, поскольку в нем есть член с именем f3. Однако Super.f3(String) и Super.f3(int,int) не применимы к f3(0), поэтому возникает ошибка времени компиляции. Обратите внимание, что класс Test не ищется.

Выбор для поиска иерархии суперкласса вложенного класса до того, как лексически заключенную область видимости называют "правилом гребня" ( §15.12.1 ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...