Eclipse предупреждение о синтетическом аксессоре для частных статических вложенных классов в Java?

Мой коллега предложил сделать несколько настроек форматирования кода и предупреждений в Eclipse более строгими. Большинство этих изменений имеют смысл, но я получаю одно странное предупреждение в Java. Вот некоторый тестовый код для воспроизведения "проблемы":

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass
    {
        public void doSomething() {}
    }

    final private InnerClass anInstance;

    {
        this.anInstance = new InnerClass();   // !!!
        this.anInstance.doSomething();
    }
}
// using "this.anInstance" instead of "anInstance" prevents another warning,
// Unqualified access to the field WeirdInnerClassJavaWarning.anInstance    

Строка с!!! выдает мне это предупреждение в Eclipse с моими новыми настройками предупреждений:

Доступ к вмещающему конструктору WeirdInnerClassJavaWarning.InnerClass() эмулируется синтетическим методом доступа. Увеличение его видимости улучшит вашу производительность.

Что это значит? Предупреждение исчезает, когда я меняю "закрытый статический класс" на "защищенный статический класс", что для меня не имеет смысла.


редактировать: я наконец понял, "правильное" исправление. Реальная проблема здесь заключается в том, что в этом вложенном приватном статическом классе отсутствует общедоступный конструктор. Этот твик убрал предупреждение:

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass
    {
        public void doSomething() {}
        public InnerClass() {}
    }

    final private InnerClass anInstance;

    {
        this.anInstance = new InnerClass();
        this.anInstance.doSomething();
    }
}

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

Я до сих пор не понимаю, почему сделать вложенный класс защищенным, а не закрытым, - это еще один метод исправления "проблемы", но, возможно, это странная ошибка Eclipse.

(извините, я должен был назвать это NestedClass вместо InnerClass, чтобы быть более ясным.)

6 ответов

Решение

Вы можете избавиться от предупреждения следующим образом:

package com.example.bugs;

public class WeirdInnerClassJavaWarning {
    private static class InnerClass {
        protected InnerClass() {}  // This constructor makes the warning go away
        public void doSomething() {}
    }

    final private InnerClass anInstance;
    {
        this.anInstance = new InnerClass(); 
        this.anInstance.doSomething();
    }
}

Как уже говорили другие, Eclipse жалуется, потому что закрытый класс без явного конструктора не может быть создан извне, кроме как через синтетический метод, который создает компилятор Java. Если вы возьмете свой код, скомпилируете его, а затем декомпилируете с помощью jad (*), вы получите следующее (переформатированное):

public class Test {
  private static class InnerClass {
    public void doSomething() {}
    // DEFAULT CONSTRUCTOR GENERATED BY COMPILER:
    private InnerClass() {}

    // SYNTHETIC METHOD GENERATED BY THE JAVA COMPILER:    
    InnerClass(InnerClass innerclass) {
      this();
    }
  }

  public Test() {
    anInstance.doSomething();
  }

  // Your instance initialization as modified by the compiler:
  private final InnerClass anInstance = new InnerClass(null);
}

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

(*) Что касается jad, я ссылался на страницу Википедии... срок действия домена, на котором размещена эта программа, истек, но Википедия ссылается на другую, которую я сам не проверял. Я знаю, что есть и другие (возможно, более свежие) декомпиляторы, но это тот, который я начал использовать. Примечание: он жалуется при декомпиляции последних файлов классов Java, но все равно хорошо работает.

Кстати, параметр для отключения предупреждения находится на странице "Ошибки / предупреждения Java" в разделе "Стиль кода" и называется:

Доступ к недоступному элементу вмещающего типа

Вы не можете создать экземпляр InnerClass из WeirdInnerClassJavaWarning. Это приватно, JVM не позволил бы вам, но язык Java (по некоторым причинам) позволил бы.

Поэтому javac создаст дополнительный метод в InnerClass, который будет просто возвращать новый InnerClass(), что позволит вам создавать экземпляры InnerClass из WeirdInnerClassJavaWarning.

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

Я до сих пор не понимаю, почему сделать вложенный класс защищенным, а не закрытым, - это еще один метод исправления "проблемы", но, возможно, в этом и заключается странная ошибка Eclipse.

Это не причуда Eclipse, а просто особенность Java. Спецификация языка Java, 8.8.9 гласит:

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

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

javac -XD-printflat WeirdInnerClassJavaWarning.java -d tmp

Необработанный вывод, компилятор добавил комментарии. Обратите внимание на добавление синтетического пакета частного класса и конструктора.

public class WeirdInnerClassJavaWarning {
    {
    }

    public WeirdInnerClassJavaWarning() {
        super();
    }
    {
    }
    private final WeirdInnerClassJavaWarning$InnerClass anInstance;
    {
        this.anInstance = new WeirdInnerClassJavaWarning$InnerClass(null);
        this.anInstance.doSomething();
    }
}

class WeirdInnerClassJavaWarning$InnerClass {

    /*synthetic*/ WeirdInnerClassJavaWarning$InnerClass(WeirdInnerClassJavaWarning$1 x0) {
        this();
    }

    private WeirdInnerClassJavaWarning$InnerClass() {
        super();
    }

    public void doSomething() {
    }
}

/*synthetic*/ class WeirdInnerClassJavaWarning$1 {
}

You should be able to get rid of it by using the default scope instead of private or protected, ie

static class InnerClass ...

Стоит также отметить, что, поместив курсор на строку кода с предупреждением и нажав ctrl-1, Eclipse сможет исправить это автоматически.

Другие вопросы по тегам