Почему меня не предупреждают об этой проблеме? - PullRequest
3 голосов
/ 04 июня 2010

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

public class A {

   public A() {
      foo();
   }

   public void foo() {
      System.out.println("A");
   }
}

public class B extends A {

   private String bar;

   public B() {
      bar = "bar";
   }

   @Override
   public void foo() {
      System.out.println(bar);
   }

}

А потом я создаю экземпляр B следующим образом:

A test = new B();

Так почему же компилятор или IDE не могут предупредить меня о том, что в методе foo класса B будет указан NullPointer? Это было бы не сложно проверить и иногда очень полезно.

Ответы [ 6 ]

7 голосов
/ 04 июня 2010

Хотя это ошибка design , это не грамматическая ошибка.

Вот цитата из Effective Java 2nd Edition, Item 17: Разработка и документация для наследования, или же запретить его :

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

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

UR: неинициализированный метод чтения поля, вызванный из конструктора суперкласса (UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR)

Этот метод вызывается в конструкторе суперкласса. На данный момент поля класса еще не инициализированы.

4 голосов
/ 04 июня 2010

Это классическая ошибка. Не используйте экземпляры методов в конструкторах.

Вы можете посмотреть на PMD, особенно правило "ConstructorCallsOverridableMethod".

http://pmd.sourceforge.net/rules/design.html

3 голосов
/ 04 июня 2010

Что не так: вы вызываете метод foo в конструкторе A, но в тот момент, когда вы его вызываете, объект еще не полностью построен. Конструктор B еще не выполнен, поэтому bar по-прежнему имеет значение по умолчанию null. Это демонстрирует, почему плохая идея вызывать неконечные методы экземпляра из конструктора.

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

Вы можете использовать инструмент статического анализа кода, например FindBugs или PMD , чтобы найти подобные проблемы в своем коде.

2 голосов
/ 04 июня 2010

И сказал отец Джош Блох в своей книге «Эффективная Ява»:

Вы не должны вызывать виртуальные методы из конструктора в Java.

Это может привести к той проблеме, которую вы наблюдаете. На момент вызова B.foo() B еще не инициализирован, поэтому B.bar равно null.

0 голосов
/ 04 июня 2010

Задача компилятора - не проверять все «не слишком сложно проверить» вещи, которые могут быть или не быть ошибкой программирования (а их сотни).

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

Это действительно то, для чего предназначены средства проверки статического стиля, такие как FindBugs. И - сюрприз! - есть проверка именно на это условие:

UR: неинициализированный метод чтения поля, вызванный из конструктора суперкласса

0 голосов
/ 04 июня 2010

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

Вы должны попытаться объявить поля окончательными, если это возможно, что очень помогает для этого и многих других вещей. В качестве альтернативы напишите свой код для защиты от пустых значений (т. Е. «Test» .equals (foo) вместо foo.equals («Test») или явных нулевых проверок)

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