Как исправить "Преодолеваемый метод конструктора"

У меня есть следующая настройка, которая дает мне сообщение о том, что "Constructor Calls Overridable Method". Я знаю, что это происходит, но мой вопрос заключается в том, как исправить это, чтобы код все еще работал, и сообщение уходит.

public interface Foo{
   void doFoo();
}
public class FooImpl implements Foo{
 @Override{
 public void doFoo(){
    //.. Do important code
 }
}
public class Bar{
  private FooImpl fi;
  public Bar(){
    fi = new FooImpl();
    fi.doFoo(); // The message complains about this line
  }
}

Спасибо!

Ответ 1

Как сообщает @Voo,

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

Из Эффективное Java 2nd Edition, пункт 17: Дизайн и документ для наследования, а также запретить его:

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

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

Конструкторы должны вызывать только те методы, которые являются окончательными или частными

Вы можете использовать статические методы factory, чтобы исправить проблему, с которой вам нужно создавать свои объекты из Bar class.

Эффективная Java, пункт 1: рассмотрим статические методы factory вместо конструкторов

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

Итак, вы идете иметь интерфейс:

public interface Foo {
     void doFoo();
}

и реализация:

public class FooImpl implements Foo {
   @Override
   public void doFoo() {
   //.. Do important code
   }
}

Чтобы создать свой класс с помощью метода factory, вы можете работать следующим образом:

  • Используйте интерфейс, чтобы определить переменную вашего класса private Foo fi вместо private FooImpl fi, используя интерфейсы над конкретными типами, является ключом к хорошему инкапсуляции и ослаблению связи вашего кода.

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

    private Bar() {       // Запрещает создание экземпляра   }

  • Удалите все вызовы для переопределения методов, присутствующих в вашем конструкторе.

  • Создайте свой статический метод factory

Наконец, вы получите класс Bar с помощью метода factory, например:

public class Bar {
    private Foo fi;

    private Bar() {// Prevents instantiation
        fi = new FooImpl();
    }

    public static Bar createBar() {
        Bar newBar = new Bar();
        newBar.fi.doFoo(); 

        return newBar;
    }
}

Мой босс говорит: "Предупреждения сонара касаются симптомов, а не болезни. Лучше всего, когда вы можете лечить болезнь".

Ответ 2

Вы можете объявить doFoo окончательным, если вам не нужно переопределять этот метод позже:

public final void doFoo() { }

Ответ 3

Ваша IDE сообщает вам об этом, потому что это потенциально опасно. Вы можете предоставить любую имплиентацию или doFoo и сделать все объекты Bar различным при запуске. В большинстве случаев это кажется плохим выбором дизайна.

Похоже, вы используете шаблон стратегии в конструкторе. Нецелесообразно использовать стратегию или любое другое избыточное поведение в конструкторе. Используйте его где-нибудь еще.

Ответ 4

Источник ошибки, который вы видите, PMD (поиск там для "overr" ), и при построении вашего примера снова это предупреждение не запускается этой версией PMD (4.2.6). Sonar просто интегрирует PMD, Checkstyle и другие инструменты и дает обзор. Поэтому проверьте, какую версию Sonar (и PMD) вы используете.

Вы можете посмотреть на это в Sonar: Sonar > Quality Profiles > Search for "overr" следует выделить правило, которое вы используете.

В Sonar вы можете проверить, какую версию PMD вы используете. Перейдите в Sonar > Configuration > Update Center и посмотрите там версию PMD, которую вы используете.