Подтвердить что ты не робот

Try/catch vs null check в java

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

public void printIt(Object1 a){
  if (a!=null){
     SubObject b= a.getB();
     if (b!=null){
         SubObject2 c=b.getC();
         if(c!=null){
             c.print();
         }
     }
  }
}

когда мне не нужно знать, что не удалось, и если что-то пустое, ничего не делать, подход может быть

public void printIt(Object1 a){
    try{
      a.getB().getC().print();
    }catch (NullPointerException e) {
    }
}

Что-то не так во второй форме, например, производительность или другие проблемы?

4b9b3361

Ответ 1

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

Аналогично, глубоко вложенные if -стояния приводят к трудно читаемому коду, и под ним все такое же "нарушение" существует, и циклическая сложность таких методов высока.

public void printIt(Object1 a) {
    if (null == a) {
        return;
    }

    SubObject b = a.getB();
    if (null == b) {
        return;
    }

    SubObject2 c = b.getC();
    if (null == c) {
        return;
    }

    c.print();
}

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

Ответ 2

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

Не используйте исключения для нормального потока управления. Эффективный пункт Java 57: используйте исключения только для исключительных ситуаций.

== UPDATE ==

Даже игнорируя проблемы с производительностью (исключения быстрее, чем когда-то, в соответствии с моим benchmark, но не так быстро, как просто если проверка), это действительно кодово-вонючий, чтобы использовать исключения для стандартного потока программы, как это. Байт-код JVM имеет специальные оптимизации, которые он может выполнять для нулевых проверок даже в операторах if. Первый пример кода является очень предпочтительным.

Ответ 3

public void printIt(Object1 a){
    if(a==null){
        throw new IllegalArgumentException("a was null, but this is not allowed here."),
    }
    [...]

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

Поэтому, если бы мне пришлось выбирать между вашим а) и вашим б), я бы выбрал а). Но если a не должно быть нулевым, вы могли бы скрыть ошибку.

Ответ 4

Неправильная часть второй версии заключается в том, что когда NPE происходит внутри getB(), getC(), он будет игнорироваться. Как уже упоминалось, исключения относятся к исключительным случаям.

Ответ 5

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

Вложенные ifs не являются определением красоты, но дадут вам возможность печатать дополнительную информацию, например "B is null" и т.д.

Ответ 6

Ответ заключается в использовании версии А.

Обычно считается "плохим дизайном" для использования Исключения для управления потоком. Исключение составляют "исключительные", особенно NPE, которые полностью можно избежать, используя нулевые проверки. Кроме того, используя нулевые проверки, вы можете указать (т.е. Журнал), какой термин является нулевым (вы не будете знать, где нуль с вашей версией B).

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

При переходе на Java 8 вы можете использовать Optional и Lambdas. Во-первых, вам нужно переписать свои классы, чтобы вернуть Optional каждого типа:

class Object1 {
    private SubObject b;

    Optional<SubObject> getB() {
        return Optional.ofNullable(b);
    }
}

class SubObject {
    private SubObject2 c;

    Optional<SubObject2> getC() {
        return Optional.ofNullable(c);
    }
}

class SubObject2 {
    @Override
    public String toString() {
        return "to be printed";
    }
}

Теперь вы можете связать вызовы без риска NullPointerExceptions в краткой форме:

a.getB()
    .flatMap(SubObject::getC)
    .ifPresent(System.out::println);

См. статью Oracle Устали от исключений Null Pointer? Рассмотрите возможность использования Java SE 8 Дополнительно! для получения дополнительной информации.

Ответ 10

Использование Java 8 необязательно:

Optional.ofNullable(a)
            .map(Object1::getB)
            .map(SubObject::getC)
            .ifPresent(Object2::print);