Как отследить исключение NullPointerException в цепочке геттеров

StackOverflow https://stackoverflow.com/questions/410890

Вопрос

Если я получу исключение NullPointerException в таком вызове:

someObject.getSomething().getSomethingElse().
    getAnotherThing().getYetAnotherObject().getValue();

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

Exception in thread "main" java.lang.NullPointerException
at package.SomeClass.someMethod(SomeClass.java:12)

Мне довольно сложно выяснить, какой из вызовов на самом деле возвращает значение null, и мне часто приходится рефакторить код примерно так:

Foo ret1 = someObject.getSomething();
Bar ret2 = ret1.getSomethingElse();
Baz ret3 = ret2.getAnotherThing();
Bam ret4 = ret3.getYetAnotherOject();
int ret5 = ret4.getValue();

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

Некоторые из вас могут возразить, что объединение геттеров — это плохой стиль, и его в любом случае следует избегать, но мой вопрос таков:Могу ли я найти ошибку, не меняя код?

Намекать:Я использую eclipse и знаю, что такое отладчик, но не могу понять, как его применить к проблеме.

Мой вывод по ответам:
Некоторые ответы говорили мне, что мне не следует связывать геттеры один за другим, некоторые ответы показывали, как отлаживать мой код, если я не подчинился этому совету.

Я принял ответ, который научил меня, когда именно объединять геттеры:

  • Если они не могут вернуть ноль, связывайте их так долго, как вам нравится.Нет необходимости проверять != null, не нужно беспокоиться о NullPointerExceptions (имейте в виду, что связывание по-прежнему нарушает закон Деметры, но я могу с этим жить)
  • Если они могут возвращать ноль, никогда, никогда не объединяйте их в цепочку и выполняйте проверку на наличие нулевых значений для каждого из них, который может возвращать ноль.

Это делает любые полезные советы по фактической отладке бесполезными.

Это было полезно?

Решение

Ответ зависит от того, как вы рассматриваете (контракт) своих геттеров.Если они смогут вернуться null вам действительно следует каждый раз проверять возвращаемое значение.Если геттер не должен вернуться null, геттер должен содержать проверку и выдать исключение (IllegalStateException?) вместо возвращения null, что ты обещал никогда не возвращаться.Трассировка стека укажет вам на точный метод получения.Вы даже можете поместить неожиданное состояние, найденное геттером, в сообщение об исключении.

Другие советы

NPE — самое бесполезное исключение в Java.Кажется, что он всегда реализуется лениво и никогда не говорит точно, что его вызвало, даже такая простая вещь, как «класс x.y.Z имеет значение null», очень поможет в отладке таких случаев.

В любом случае, единственный хороший способ найти метатель NPE в этих случаях — это следующий вид рефакторинга:

someObject.getSomething()
          .getSomethingElse()
          .getAnotherThing()
          .getYetAnotherObject()
          .getValue();

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

В IntelliJ IDEA вы можете установить точки останова исключения.Эти точки останова срабатывают всякий раз, когда генерируется указанное исключение (вы можете ограничить его пакетом или классом).

Таким образом, вам будет легко найти источник вашего NPE.

Я предполагаю, что вы можете сделать что-то подобное в netbeans или eclipse.

РЕДАКТИРОВАТЬ: Здесь это объяснение того, как добавить точку останова исключения в eclipse.

Если вы часто пишете:

a.getB().getC().getD().getE();

вероятно, это запах кода, и его следует избегать.Вы можете провести рефакторинг, например, в a.getE() который вызывает b.getE() который вызывает c.getE() который вызывает d.getE().(Этот пример может не иметь смысла для вашего конкретного случая использования, но это один из шаблонов для устранения этого запаха кода.)

См. также Закон Деметры, в котором говорится:

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

Поэтому не следует иметь цепочку сообщений, например. a.getB().getC().doSomething().Следование этому «закону» имеет гораздо больше преимуществ, помимо упрощения отладки NullPointerExceptions.

Обычно я не объединяю такие геттеры в цепочку, где есть более одного геттера, допускающего значение NULL.

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

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

Тем временем мы можем мечтать о оператор безопасной навигации groovy

Ранний отказ также возможен.

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

public Foo getSomething()
{
  Foo result;
  ...
  if (result == null) {
    throw new IllegalStateException("Something is missing");
  }
  return result;
}

Вот как найти ошибку с помощью Eclipse.

Сначала установите точку останова на строке:

someObject.getSomething().getSomethingElse().
getAnotherThing().getYetAnotherObject().getValue();

Запустите программу в режиме отладки, позвольте отладчику переключиться на свою перспективу при попадании в строку.

Теперь выделите «someObject» и нажмите CTRL+SHIFT+I (или щелкните правой кнопкой мыши и скажите «проверить»).

Это ноль?Вы нашли свой NPE.Это ненулевое значение?Затем выделите someObject.getSomething() (включая круглые скобки) и проверьте его.Это ноль?И т. д.Продолжайте двигаться по цепочке, чтобы выяснить, где находится ваш NPE, без необходимости менять код.

Вы можете обратиться к этот вопрос об избежании != null.

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

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

Если у вас есть метод или конструктор, который принимает параметр объекта, и рассматриваемый объект/метод не может разумно обработать значение этого параметра, равное нулю, просто проверьте и тут же выдайте исключение NullPointerException.

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top