Является ли перехват исключений нулевого указателя запахом кода? - PullRequest
57 голосов
/ 06 апреля 2010

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

Однако перехват NullPointerException мне показался неправильным. На мой взгляд, исключения нулевого указателя являются результатом неправильного кода и не являются ожидаемым исключением в системе.

Есть ли случаи, когда имеет смысл перехватить исключение нулевого указателя?

Ответы [ 18 ]

36 голосов
/ 06 апреля 2010

Да, ловить любой RuntimeException - это почти всегда запах кода. C2 Wiki , похоже, согласен.

Исключением, вероятно, будут некоторые особо защитные части кода, которые запускают в значительной степени случайный код из других модулей. Примерами таких защитных структур могут быть EDT , ThreadPools / Executors и система плагинов.

24 голосов
/ 06 апреля 2010

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

catch (NullPointerException) {
    ApplyPainfulElectricShockToProgrammer();
}
15 голосов
/ 06 апреля 2010

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

В этом случае OK его перехватить, иначе - нет.

7 голосов
/ 06 апреля 2010

Это зависит.

Насколько опытен этот сотрудник? Он делает это из-за невежества / лени или для этого есть веская причина? (как это главный поток превыше всего и никогда не должен умирать?)

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

Но при некоторых обстоятельствах перехват NullPointerException может быть приемлемым.

3 голосов
/ 06 апреля 2010

В общем, я думаю, что это запах кода; мне кажется, что защитные проверки лучше. Я бы расширил это, чтобы охватить большинство непроверенных исключений, за исключением циклов событий и т. Д., Которые хотят перехватывать все ошибки для отчетов / ведения журнала.

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

3 голосов
/ 27 февраля 2014

Это плохо, но это может привести к оптимизированному байт-коду.

Если Integer i в большинстве случаев не равен null, то проверка снижает общую производительность.Сам чек стоит 3 инструкции (0-4).Затем весь случай занимает 7 инструкций (0-14).

public class IfNotNull {

    public Integer i;

    public String getIAsString() {
        if (i != null) {
            return i.toString();
        } else {
            return "";
        }
    }
}

  public java.lang.String getIAsString();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field i:Ljava/lang/Integer;
       4: ifnull        15
       7: aload_0       
       8: getfield      #2                  // Field i:Ljava/lang/Integer;
      11: invokevirtual #3                  // Method java/lang/Integer.toString:()Ljava/lang/String;
      14: areturn       // <- here we go
      15: ldc           #4                  // String 
      17: areturn 

Следуя подходу EAFP , который распространен в мире Python.Случай null будет дорогостоящим, но нам нужно всего 4 инструкции (0-7) для случая not null.

public class TryCatch {

    public Integer i;

    public String getIAsString() {
        try {
            return i.toString();
        } catch (NullPointerException npe) {
            return "";
        }
    }
}

  public java.lang.String getIAsString();
    Code:
       0: aload_0       
       1: getfield      #2                  // Field i:Ljava/lang/Integer;
       4: invokevirtual #3                  // Method java/lang/Integer.toString:()Ljava/lang/String;
       7: areturn       // <- here we go
       8: astore_1      
       9: ldc           #5                  // String a
      11: areturn       
    Exception table:
       from    to  target type
           0     7     8   Class java/lang/NullPointerException

Кто знает, может ли JIT-компилятор оптимизировать это?

3 голосов
/ 07 апреля 2010

Забавно

Я только что нашел то, что не следует делать на работе:

public static boolean isValidDate(final String stringDateValue) {
    String exp = "^[0-9]{2}/[0-9]{2}/[0-9]{4}$";
    boolean isValid = false;
    try {
        if (Pattern.matches(exp, stringDateValue)) {
            String[] dateArray = stringDateValue.split("/");
            if (dateArray.length == 3) {
                GregorianCalendar gregorianCalendar = new GregorianCalendar();
                int annee = new Integer(dateArray[2]).intValue();
                int mois = new Integer(dateArray[1]).intValue();
                int jour = new Integer(dateArray[0]).intValue();

                gregorianCalendar = new GregorianCalendar(annee, mois - 1,
                        jour);
                gregorianCalendar.setLenient(false);
                gregorianCalendar.get(GregorianCalendar.YEAR);
                gregorianCalendar.get(GregorianCalendar.MONTH);
                gregorianCalendar.get(GregorianCalendar.DAY_OF_MONTH);
                isValid = true;
            }
        }
    } catch (Exception e) {
        isValid = false;
    }
    return isValid;
}

baaad:)

Разработчик хотел, чтобы календарь вызывал исключениятакого рода:

java.lang.IllegalArgumentException: DAY_OF_MONTH
    at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2316)
    at java.util.Calendar.updateTime(Calendar.java:2260)
    at java.util.Calendar.complete(Calendar.java:1305)
    at java.util.Calendar.get(Calendar.java:1088)

для аннулирования значений ...

Да, это работает, но это не очень хорошая практика ...

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

2 голосов
/ 06 апреля 2010

Конечно, это так.

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

Оператор if требует выполнения, возможно, трех инструкций и является локальной проверкой (т.е. вы делаете проверку в том же месте, где вам нужна гарантия).

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

try
  return contacts.find("Mom").getEmail()
catch (NullPointerException e)
  return null

Вы не знаете, был ли NPE брошен в 'getEmail' или в 'find'.

Технически худшее решение для очень, очень распространенного шаблона, написанного более запутанным способом?Это не звание, но оно определенно пахнет плохо: /

2 голосов
/ 06 апреля 2010

Единственное место, где вы должны поймать исключение NullPointerException (или, в частности, просто Throwable), находится на некотором верхнем уровне или системной границе, чтобы ваша программа не полностью зависала и могла восстановиться. Например, настройка страницы с ошибкой в ​​вашем файле web.xml обеспечивает универсальный контроль, позволяющий веб-приложению восстанавливаться после исключения и уведомлять пользователя.

1 голос
/ 21 декабря 2013

Как насчет этого:

try
{
    foo.x = bar;
}
catch (NullPointerException e)
{
    // do whatever needs to be done
}

как микрооптимизации, когда foo может быть нулевым, но почти никогда не равен?

Идея такова:

  • Для явной проверки NULL требуется одна машинная инструкция

  • С другой стороны, проверка NULL во второй версии может быть выполнена путем разрешения доступа NULL, перехвативSIGSEGV и выбрасывает исключение NullPointerException.Это бесплатно, если объект не NULL.

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