Можно ли сгенерировать исключение NullPointerException программно?

Когда есть постусловие, это возвращаемое значение метода не должно быть нулевым, что можно сделать?

я мог бы сделать

assert returnValue != null : "Not acceptable null value";

но утверждения можно отключить!

Так можно ли делать

if(returnValue==null)
      {
           throw new NullPointerException("return value is null at method AAA");
      }

?

Или лучше использовать пользовательское исключение (например, NullReturnValueException) для такого условия?

19 ответов

Решение

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

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

Как бы ты это задокументировал? "Этот метод генерирует исключение NullPointerException, если возвращаемое значение неожиданно равно нулю"? Не объясняя, как это могло произойти? Нет, я бы использовал здесь утверждение. Исключения следует использовать для ошибок, которые могут предположительно произойти, - чтобы не охватывать вещи, которые могут произойти, если в методе что-то не так, потому что это никому не поможет.

Я бы порекомендовал вам никогда не бросать NullPointerException самостоятельно.

Основная причина не делать этого, как говорит Турбьерн Равн Андерсен в комментарии ниже, заключается в том, что вы не хотите смешивать "настоящие, плохие NPE" с намеренно выброшенными NPE.

Итак, пока вы не уверены, что можете распознать "действительный" NPE, я бы рекомендовал использовать IllegalArgumentException когда вы хотите сказать своему пользователю API, что null не является допустимым значением аргумента. Поведение вашего метода при передаче недопустимого нулевого параметра должно быть задокументировано.

Другим (более современным imho) вариантом является использование @NotNull аннотация рядом с аргументом. Вот статья об использовании аннотации @NotNull.

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

Например, если вы используете библиотеку с модулем предусловий, например, Guavaтогда я нахожу с помощью checkNotNull()Подобные методы - предпочтительный способ иметь дело с незаконно переданными нулями.

checkNotNull(arg, msg) бросает NPE, но из стека трассировки совершенно ясно, что он был произведен Preconditions.checkNotNull() и, таким образом, это не неизвестная ошибка, а ожидаемое поведение.

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

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

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

Исходя из моего опыта, Java-программисты очень быстро узнают, что это исключение вызвано ошибкой в ​​коде, поэтому создание этого вручную будет крайне запутанным для большинства из них. IllegalArgumentException Это лучшая идея, когда вы передаете недопустимый аргумент (например, null, где что-то не должно быть null).

Это также вызывает другую эвристику. NPE = кто-то сделал ошибку в коде здесь, IllegalArgumentException = объект, данный методу, недопустим.

С другой стороны, Javadoc говорит:

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

Так что бросать NPE было бы законно, однако это не обычная практика, поэтому я бы порекомендовал IllegalArgumentException,

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

catch (NullPointerException npe) {
  if (npe.getMessage().equals("Null return value from getProdByCode") {
    drawToUser("Unable to find a product for the product type code you entered");
  } 
}

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

http://pmd.sourceforge.net/pmd-5.0.1/rules/java/strictexception.html
"Избегайте выбрасывания исключений NullPointerException. Это сбивает с толку, потому что большинство людей предположит, что виртуальная машина их выбросила. Попробуйте вместо этого использовать IllegalArgumentException; это будет ясно рассматриваться как исключение, инициированное программистом".

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

Если вы проверите параметры вашего метода в самом начале, throw new IllegalArgumentException("foo==null") для меня тоже приемлемо.

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

JavaDoc для NullPointerException заявляет:

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

* Calling the instance method of a null object.
* Accessing or modifying the field of a null object.
* Taking the length of null as if it were an array.
* Accessing or modifying the slots of null as if it were an array.
* Throwing null as if it were a Throwable value. 

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

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

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

Книга, которую я назвал Java О'Рейли в двух словах, написанная экспертом, перечисляет это определение для NullPointerException:

Сигнализирует о попытке получить доступ к полю или вызвать метод нулевого объекта.

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

Абсолютно да.

Даже JDK7 разрешают это. Смотрите Объекты #requireNonNull

void doWith(final Object mustBeNotNull) {

    /*
    // bush style
    if (mustBeNotNull == null) {
        throw new IllegalArgumentException("mustBeNotNull must not be null");
    }
    */

    /*
    // obama style
    if (mustBeNotNull == null) {
        throw new NullPointerException("mustBeNotNull must not be null");
    }
    */

    // kangnam style
    Objects.requireNonNull(mustBeNotNull, "mustBeNotNull must not be null");

    assert mustBeNotNull != null;
}

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

Несмотря на неосведомленные отрицательные голоса, в JDK есть много методов, которые делают именно это.

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

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

Проблема с Java-программистами и null заключается в том, что люди происходят из C/C++, где NULL означает нечто совсем другое. В C/C++ разыменование NULL (или дикого) указателя является серьезной проблемой, которая может вызвать странные проблемы с памятью или привести к сбою вашей программы (что явно нежелательно). Если вы можете отказаться от мышления C/C++ и понять, что у вас есть этот дополнительный уровень, JVM, который обрабатывает это условие, вы начинаете думать о NULL немного по-другому.

Например, в C++ есть ссылки, которым никогда нельзя присвоить NULL. В Java нет ссылок, но параметры объекта Java ведут себя больше как указатели C++. Но в Java существует множество ситуаций, в которых метод неявно НЕ должен получать нулевое значение для параметра! Так что же нам делать?

Проблема с обработкой нуля в Java, как вы делаете в C++, состоит в том, что это приводит к проверкам нуля ВСЕГДА, тогда как в C++ вы просто объявляете метод для получения ссылки, который явно заявляет, что он не принимает NULL. Довольно скоро у каждого метода должна быть проверка работоспособности, чтобы утверждать состояние программы на тот момент, создавая беспорядок.

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

Зачем? Что ж, давайте посмотрим, что происходит, когда такой метод получает значение NULL в качестве значения параметра. а) Возможно, все в порядке, потому что оно не разыменовывает значение как часть своего поведения. В этом случае ничего не происходит. б) Значение разыменовывается. В этом случае поведение JVM является именно тем, что мы хотим: генерируется исключение, указывающее, что контракт метода был нарушен из-за значения параметра, равного NULL, и он включает в себя трассировку стека, которая ведет нас полностью к строка в методе, где значение используется таким образом.

Люди не согласны с NPE, потому что думают, что когда вы видите NPE в журналах, это означает "кто-то облажался". Но давайте подумаем об этом на секунду. В чем на самом деле разница в NPE как индикаторе хулиганства в отличие от ожидаемого поведения? Я бы сказал, что основное различие (и преимущество) использования NPE в качестве ожидаемого поведения состоит в том, что он указывает не на метод, в котором он произошел, а на вызывающую сторону за нарушение контракта метода. Это гораздо более полезная информация. Если бы мы просто проверили null и сгенерировали другое исключение, у нас могло бы быть ложное впечатление, что наблюдаемое поведение является ожидаемой ошибкой, когда действительно вызывающий объект нарушает контракт метода. Поздравляем, вы правильно предвидели, как вызывающая сторона может ошибиться при вызове метода - однако все, что вы сделали, - сбились с пути относительно истинной причины исключения - или, в лучшем случае, вы используете два разных класса исключений. указывать на то же самое и массово загрязнять код ненужным мусором.

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

Некоторые примеры:

public void foo(Object o) {
  if (o == null) {
    throw new IGotchaException("HA! You're an IDIOT! I knew it!!");
  }
  o.bar();
}

public void foo(Object o) {
  o.bar();
}

^ Политически разные. Функционально не так уж и много.

public void foo(int a, long b, double c, Object o) {
  if (o == null) {
    throw new IllegalArgumentException("Oh. Uh. Well, you've passed me null here. I... I'm not sure where to go from here because this object is kind of required for this function to do what it's supposed to do. Soooo... wouldja mind reworking your code a little bit so as to not pass null to this function as a parameter?! That'd be great thanks. Oh by the way, it's cause we deference o 40 lines below here");
  }
  // ...
  o.doSomethingWithA(a);
}

public void foo(int a, long b, double c, Object o) {
  // ...
  o.doSomethingWithA(a);
  // NullPointerException, line 40, e.g. it wasn't OK to pass null for o you lunkhead
}

^ Возможно, экономит несколько циклов процессора за счет большого количества надоедливого кода. Тем не менее, мы делаем меньше сравнений во втором случае.

public void foo(Object a, Object b, Object c, Object d) {
  if (a == null) throw IllegalArgumentException("jackass");
  if (b == null) throw IllegalArgumentException("jackass");
  if (c == null) throw IllegalArgumentException("jackass");
  // But d is totally OK!
  // ...
  c.setSomeValueThatMayBeNull(d);
}

public void foo(Object a, Object b, Object c, Object d) {
  // ...
  c.setSomeValueThatMayBeNull(d);
  // Throws null pointer exception if c is null, but not if d is null. Which is basically the same as above
}

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

public void foo(Object o) {
  if (o == null) {
    doTheBoogie();
  } else {
    doTheRobot();
  }
}

^ Плохо

public void foo(Object o, int b) {
  Bar value = o.findSomethingWhichMayExist(b);
  if (value == null)
    return;
  value.doSomething();
}

^ Использование нулевого возвращаемого значения для указания на отсутствие значения. ХОРОШО.

Еще одна причина, по которой у людей возникают проблемы с NPE, заключается в том, что они не знают, как обрабатывать исключения. NPE никогда не должен быть демонстратором. Надлежащее поведение - перехватывать RuntimeException, предположительно на гораздо более высоком (или более низком, в зависимости от того, как вы это видите) уровне в стеке вызовов, который перехватывает его и сообщает об этом перед "main". То есть, если вы разрабатываете программу, которая должна быть более устойчивой и не может просто аварийно завершить работу, когда происходит NPE.

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

Когда есть постусловие, это возвращаемое значение метода не должно быть нулевым, что можно сделать?

Постусловие означает, что рассматриваемый метод имеет ошибку, если условие не выполняется. Способ выразить это в коде с помощью assert на пост-состоянии. Непосредственно выбрасывая исключение, такое как NullPointerException или IllegalStateException, был бы немного дезинформирующим, и, следовательно, ошибочным.

Можно ли сгенерировать исключение NullPointerException программно?

Документ API Java для NPE говорит "да", но, судя по голосам на этой странице, большинство разработчиков 3:1 говорит "нет". Так что я бы сказал, что это зависит от соглашений в вашей рабочей группе.

Документ API сначала перечисляет случаи, когда JVM вызывает NPE, потому что код пытается вызвать операцию с нулевой ссылкой, которая требует какого-либо объекта (например, вызова метода или доступа к полю), и null это не объект. Затем говорится:

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

Что интересно, null здесь называется "объект", который это не так. Что напоминает мне, что само имя NullPointerException странно для языка, который не имеет указателей. (Это, вероятно, должно было быть NullReferenceException как в библиотеке классов Microsoft .NET.)

Так что нам следует отклонить документацию по API на этот счет? Я так не думаю. Библиотека классов использует NPE, как описано в документации, например, в java.nio.channels:

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

Это не NPE, сгенерированный JVM, а кодированный NPE с прикрепленным сообщением об ошибке, в котором указано, какой аргумент был null (лайк "in" is null!). (Код можно увидеть, выполнив javap -c -p java.nio.channels.Channels | more, находясь в поиске private static void checkNotNull.) И есть много классов, которые используют NPE таким образом, по существу, как особый случай IllegalArgumentException,

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

Чтобы устранить второстепенный вопрос, что NPE будет все равно брошен дальше: возможно, имеет смысл рано обнаруживать ошибки, вместо того, чтобы позволить JVM продолжить работу с программой, возможно, с использованием дискового или сетевого ввода-вывода (и задержками).) и генерирует неоправданно большой след стека.

Как однажды сказал Джошуа Блох: "Ноль - отстой!":) всякий раз, когда есть ноль - мое лицо, я стараюсь использовать опциональный, который предоставляет guava. Преимущества для меня многочисленны.

В Java-стихе null всегда является допустимым значением при ожидании объекта. Вам лучше избегать таких невозможных почтовых условий. Если вы действительно не можете принять ноль, вам придется переделать свой метод, чтобы вы могли вернуть примитив.

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

Вопрос задавался до 7 лет, но теперь у нас есть Optional в Java 8, и эта функция позволяет предотвратить NPE.

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

Бросить это исключение в некоторых случаях не очень хорошая практика, и мне интересно, почему, когда вы уже поймали его с помощью оператора if?

если (ReturnValue== NULL)

Другие вопросы по тегам