IllegalArgumentException или NullPointerException для нулевого параметра?

У меня есть простой метод установки свойства, и null не подходит для этого конкретного свойства. Меня всегда мучила такая ситуация: бросать IllegalArgumentException или NullPointerException? Из javadocs оба кажутся подходящими. Есть ли какой-то понятный стандарт? Или это всего лишь одна из тех вещей, которые вы должны делать то, что вам больше нравится, и оба они действительно правильны?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
557
0
167 220
26
Перейти к ответу Данный вопрос помечен как решенный

Ответы 26

Ответ принят как подходящий

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

Следующие ответы на этот вопрос убедительно доказывают, что NullPointerException является правильным исключением: stackoverflow.com/a/8160/372926; stackoverflow.com/a/8196334/372926; и stackoverflow.com/a/6358/372926.

SamStephens 07.02.2014 03:09

IllegalArgumentException находится в противоречии с Java Objects.requireNonNull (T) и Guava Preconditions.checkNotNull (T), который выбрасывает NullPointerException. Однако правильный ответ определенноIllegalArgumentException, как объяснено в Отличный ответ Джейсона Коэна и его раздел комментариев.

matoni 22.07.2018 17:32

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

Итак, если вы его используете, и это null, NullPointer. Если его передают, и это null, IllegalArgument.

Я склонен следовать дизайну библиотек JDK, особенно коллекций и параллелизма (Джошуа Блох, Дуг Ли, эти ребята знают, как разрабатывать надежные API). В любом случае, многие API в JDK активно выбрасывают NullPointerException.

Например, Javadoc для Map.containsKey гласит:

@throws NullPointerException if the key is null and this map does not permit null keys (optional).

Совершенно верно бросить свой собственный NPE. По соглашению в сообщение об исключении следует включать имя параметра, которое было пустым.

Схема идет:

public void someMethod(Object mustNotBeNull) {  
    if (mustNotBeNull == null) {  
        throw new NullPointerException("mustNotBeNull must not be null");  
    }  
}

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

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

Gili 29.12.2010 23:11

@Gili: возможно, эта проблема существует в первую очередь потому, что Java не поддерживает множественное наследование. Если Java поддерживает MI, вы сможете создать исключение, наследуемое как от IllegalArgumentException, так и от NullPointerException.

Lie Ryan 01.03.2013 18:29

Сообщение не должно включать аргумент, так как он всегда будет нулевым, что дает: «null не должно быть нулевым», что не очень полезно. :-) В противном случае, я согласен, вы можете сделать "богатый" NPE, с содержательным сообщением.

PhiLho 22.07.2013 19:21

Я должен согласиться - в случае сомнений следуйте стандартному API. Не все в API оптимально, но, тем не менее, он поддерживается и повторяется сотнями разработчиков и используется миллионами программистов. Теперь в Java 7 у нас есть еще один пример использования NPE таким образом; метод Objects.requireNonNull (T obj) - явно задан для проверки того, что ссылки на объекты не равны нулю, четко задан для проверки параметров в методах / конструкторах и явно задан для выдачи NPE, если объект имеет значение NULL. Конец чего-либо

flamming_python 30.06.2014 21:48

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

Во время выполнения не будет содержательного сообщения.

mP. 31.03.2012 13:03

На самом деле этот комментарий мог вывести и другое мнение. Пусть виртуальная машина говорит на NPE, а программисты говорят на IAE раньше виртуальной машины, если они этого хотят.

Jin Kwon 13.07.2012 06:22

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

PhiLho 22.07.2013 19:26

Голосование против этого ответа - непонимание аппаратного обеспечения. Конечно, аппаратные проверки (которые выполняет ЦП) дешевле, чем явная проверка. Разыменование нуля - это особый случай SegmentationFault (SegV) (доступ к странице, не принадлежащей процессу), который CPU / OS проверяет, а JRE обрабатывает как особый случай, вызывающий исключение NullPointerException.

digital_infinity 11.08.2017 11:31

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

if ( variable == null )
    throw new IllegalArgumentException("The object 'variable' cannot be null");

У вас практически нет причин неявно использовать исключение NullPointerException. NullPointerException - это исключение, создаваемое виртуальной машиной Java при попытке выполнить код по нулевой ссылке (например, нанизывать()).

Стандарт закидывать NullPointerException. В целом безошибочная «Эффективная Java» кратко обсуждает это в статье 42 (первое издание), статье 60 (второе издание) или статье 72 (третье издание) «Поддерживайте использование стандартных исключений»:

"Arguably, all erroneous method invocations boil down to an illegal argument or illegal state, but other exceptions are standardly used for certain kinds of illegal arguments and states. If a caller passes null in some parameter for which null values are prohibited, convention dictates that NullPointerException be thrown rather than IllegalArgumentException."

Я категорически не согласен. Исключения NullPointerExceptions следует вызывать только в том случае, если JVM случайно следует за пустой ссылкой. Это разница, которая помогает вам, когда вас вызывают, чтобы посмотреть код в 3 часа ночи.

Thorbjørn Ravn Andersen 30.05.2009 00:51

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

GaryF 08.06.2009 14:28

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

Christopher Smith 09.12.2009 08:49

Согласитесь с Торбьёрном, сделайте себе одолжение и избегайте ада отладки.

bjornl 08.11.2010 16:55

Трассировка исключения показывает точку исключения, поэтому, если разница в типе исключения вызывает у вас ад или является «той разницей, которая вам помогает», вы делаете что-то очень неправильное.

Jim Balter 01.02.2013 03:34

@ ThorbjørnRavnAndersen, трассировка стека все равно укажет вам на одно и то же место? конечно, не имеет большого значения для который

djechlin 07.07.2014 06:15

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

Thorbjørn Ravn Andersen 07.07.2014 06:23

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

Thorbjørn Ravn Andersen 07.07.2014 23:56

Фантазии и софистика. Чуть ниже MB пишет: «Обратите внимание, что аргументы о жесткой отладке являются фиктивными, потому что вы, конечно, можете передать сообщение NullPointerException о том, что было пустым, а почему не должно быть пустым. Как и в случае с IllegalArgumentException».

Jim Balter 08.07.2014 01:37

Кроме того, эти вопросы были освещены, в том числе, Кристофером Смитом 4 1/2 года назад ... продолжение сбора фальшивых точек - это троллинг.

Jim Balter 08.07.2014 01:59

Как первоначальный ответчик, позвольте мне сказать, что это не имеет большого значения. Конечно, это не гарантирует шести лет разговоров. Выберите то, что вам нравится, и будьте последовательны. Стандарт, как я уже отмечал, - NPE. Если вы предпочитаете IAE по каким-либо причинам, дерзайте. Просто будьте последовательны.

GaryF 08.07.2014 12:38

именно эта цитата привела меня сюда, поскольку я не согласен ... думаю, я не найду здесь ответа. Я буду продолжать бросать IAE, а не NPE, хотя я видел, как NPE выбрасывается из метода java.sql.DriverManager.registerDriver (Driver), если Driver имеет значение null, NPE выбрасывается явно. Я задавался вопросом, ПОЧЕМУ.

Peter Perháč 16.10.2015 14:17

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

ACV 21.08.2018 12:43

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

Большая разница здесь в том, что предполагается использовать исключение IllegalArgumentException при проверке допустимости аргумента метода. Предполагается, что исключение NullPointerException должно использоваться всякий раз, когда объект «используется», когда он имеет значение NULL.

Я надеюсь, что это поможет увидеть эти два аспекта в перспективе.

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

Christopher Smith 09.12.2009 09:12

Не могу больше согласиться с тем, что говорится. Рано терпеть неудачу - быстро. Довольно хорошая мантра исключения.

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

Мои 2 цента

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

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

Если я переопределяю метод, я использую все, что использует переопределенный метод.

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

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

Donz 09.11.2010 11:56

Вы должны использовать IllegalArgumentException (IAE), а не NullPointerException (NPE) по следующим причинам:

Во-первых, NPE JavaDoc явно перечисляет случаи, когда подходит NPE. Обратите внимание, что все они выбрасываются во время выполнения, когда null используется ненадлежащим образом. Напротив, IAE JavaDoc не может быть более ясным: «Брошено, чтобы указать, что методу был передан недопустимый или несоответствующий аргумент». Ага, это ты!

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

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

В-четвертых, все другие неверные данные параметров будут IAE, так почему бы не быть последовательными? Почему незаконный null настолько особенный, что заслуживает отдельного исключения из всех других типов недопустимых аргументов?

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

Эффективное Java 2-е издание, пункт 60: «Возможно, все ошибочные вызовы методов сводятся к недопустимому аргументу или недопустимому состоянию, но другие исключения стандартно используются для определенных видов недопустимых аргументов и состояний. Если вызывающий передает значение null в каком-либо параметре, для которого пустые значения запрещены, соглашение требует, чтобы было выбрано исключение NullPointerException, а не IllegalArgumentException. Точно так же, если вызывающий объект передает значение вне допустимого диапазона в параметре, представляющем индекс в последовательность, должно быть сгенерировано исключение IndexOutOfBoundsException, а не IllegalArgumentException ».

Gili 29.12.2010 23:15

В JavaDoc для NPE также говорится следующее: «Приложения должны генерировать экземпляры этого класса, чтобы указать на другое незаконное использование нулевого объекта». Этот может быть более понятным :(

R. Martinho Fernandes 25.01.2011 19:25

К сожалению, оба метода проверки Validate.notNull (общий язык) и Preconditions.checkNotNull (guava) выдают NPE :-(

Aaron Digulla 15.10.2012 19:43

Хотя Guava также имеет Preconditions.checkArgument () выдает исключение IllegalArgumentException ...

michaelok 17.12.2013 04:02

«Когда вы видите IAE, вы предполагаете, что вызывающий метод наверху стека передал недопустимое значение. И снова последнее предположение верно, а первое вводит в заблуждение». - Это вовсе не заблуждение, скорее, наоборот. Если бы вы явно не проверили нулевое значение аргумента, вы бы получили NPE при его использовании. Проверка предназначена только для того, чтобы подробно описать логическую ошибку. Нет никакой разницы в логичный между программой, которая генерирует NPE при разыменовании null, и программой, которая генерирует исключение вместо разыменования null. Сообщение об исключении должно прояснить причину.

Jim Balter 08.07.2014 01:52

«Хотя Guava также имеет Preconditions.checkArgument () выдает исключение IllegalArgumentException» - да, так? Это требует логического условия; он не предназначен для использования с нулевыми аргументами.

Jim Balter 08.07.2014 01:55

@AaronDigulla, из документации Guava: «Мы понимаем, что есть много веских аргументов в пользу того, чтобы использовать IAE для нулевого аргумента. Фактически, если бы у нас была машина времени на более чем 15 лет назад, мы могли бы даже попытаться протолкнуть вещи в в этом направлении. Однако мы решили придерживаться предпочтения JDK и эффективной Java для NullPointerException. Если вы твердо уверены, что IAE верна, у вас все еще есть checkArgument(arg != null), только без того удобства, что он возвращает arg, или вы можете создать локальную утилиту для вашего проекта ". code.google.com/p/guava-libraries/wiki/IdeaGraveyard

Arto Bendiken 29.10.2014 02:07

Я поддерживаю этот ответ. Думаю, здесь применима фраза «Мудрость, а не правила» (javapractices.com/topic/TopicAction.do?Id=259).

jpangamarca 20.04.2016 18:18

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

Igor 24.08.2016 13:49

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

Wouter 11.04.2017 22:32

Если вы хотите быть конкретным, проверьте и бросьте IAE, если не просто позвольте NPE произойти естественным образом. Это вопрос выбора: явное (IAE) или неявное (NPE) - все в порядке, если вы никогда не бросаете NPE явно в коде :)

Pedro Borges 17.07.2018 14:45

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

matoni 21.11.2020 13:58

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

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

Christopher Smith 09.12.2009 09:09

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

Нет, есть только один правильный ответ на этот вопрос - использовать исключение throw IllegalArgument, когда ввод в метод неверен. Также в среде разработки вы можете использовать утверждения для проверки допустимости ввода и выдачи правильного исключения;)

Mr.Q 24.02.2019 15:20

@ Mr.Q И я думаю, что следует выбросить NullPointerException: это соглашение, которое использует JDK и требует для интерфейсов, оно более конкретное (как IndexOutOfBoundsException и т. д.) И т. Д.

Solomon Ucko 22.07.2019 23:28

kekekekkek ...: D

Yousha Aleayoub 25.11.2019 14:35

В этом случае IllegalArgumentException передает пользователю четкую информацию, использующую ваш API, о том, что «не должно быть нулевым». Как отмечали другие пользователи форума, вы можете использовать NPE, если хотите, до тех пор, пока вы передаете правильную информацию пользователю, используя свой API.

GaryF и tweakt сбросили ссылки на «Эффективную Java» (которой я клянусь), в которых рекомендуется использовать NPE. А изучение того, как строятся другие хорошие API, - лучший способ понять, как построить свой API.

Еще один хороший пример - взглянуть на Spring API. Например, org.springframework.beans.BeanUtils.instantiateClass (Constructor ctor, Object [] args) имеет строку Assert.notNull (ctor, «Конструктор не должен быть нулевым»). Метод org.springframework.util.Assert.notNull (объект объекта, сообщение String) проверяет, является ли переданный аргумент (объект) нулевым, и если это так, он генерирует новое исключение IllegalArgumentException (сообщение), которое затем перехватывается в организации. springframework.beans.BeanUtils.instantiateClass (...) метод.

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

Я принял подход «выбросить новое исключение IllegalArgumentException (« foo == null »)». В любом случае вам нужно зарегистрировать имя переменной (чтобы быть уверенным, что вы смотрите правильное заявление и т. д.)

Thorbjørn Ravn Andersen 30.05.2009 01:05

Apache Commons Lang имеет NullArgumentException, который выполняет ряд вещей, обсуждаемых здесь: он расширяет IllegalArgumentException, и его единственный конструктор принимает имя аргумента, который должен быть ненулевым.

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

Обратите внимание, они удалили его из commons-lang3: apache-commons.680414.n4.nabble.com/…

artbristol 18.09.2013 14:47

Проголосовал за аргумент Джейсона Коэна, потому что он был хорошо представлен. Разрешите пошагово расчленить его. ;-)

  • NPE JavaDoc явно говорит, «другое незаконное использование нулевого объекта». Если бы это было просто ограничено ситуациями, когда среда выполнения встречает нуль, а не должна, все такие случаи можно было бы определить гораздо более лаконично.

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

  • Я бы предпочел NPEIAE по нескольким причинам.

    • Более конкретно о природе незаконной операции.
    • Логика, ошибочно допускающая пустые значения, сильно отличается от логики, ошибочно допускающей недопустимые значения. Например, если я проверяю данные, введенные пользователем, если я получаю неприемлемое значение, источником этой ошибки является конечный пользователь приложения. Если я получаю ноль, это ошибка программиста.
    • Недопустимые значения могут вызывать такие вещи, как переполнение стека, ошибки нехватки памяти, исключения синтаксического анализа и т. д. Действительно, большинство ошибок обычно в какой-то момент представляют собой недопустимое значение при вызове какого-либо метода. По этой причине я рассматриваю IAE как фактически САМЫЕ ОБЩИЕ всех исключений в RuntimeException.
  • Фактически, другие недопустимые аргументы могут привести к всевозможным другим исключениям. UnknownHostException, FileNotFoundException, различные исключения синтаксических ошибок, IndexOutOfBoundsException, сбои аутентификации и т. д. И т. Д.

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

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

Thorbjørn Ravn Andersen 07.07.2014 06:33

Я был полностью за то, чтобы использовать IllegalArgumentException для нулевых параметров, до сегодняшнего дня, когда я заметил метод java.util.Objects.requireNonNull в Java 7. С этим методом вместо того, чтобы делать:

if (param == null) {
    throw new IllegalArgumentException("param cannot be null.");
}

ты можешь сделать:

Objects.requireNonNull(param);

и он выдаст NullPointerException, если переданный вами параметр - null.

Учитывая, что этот метод находится прямо посередине java.util, я считаю его существование довольно убедительным свидетельством того, что использование NullPointerException - это «способ работы Java».

Я думаю, что в любом случае я решился.

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

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

Guava Preconditions.checkNotNull(arg) также бросает NPE.

assylias 14.10.2012 20:55

На самом деле это не добавляет веса исключению NullPointerException для недопустимых нулевых АРГУМЕНТОВ. И JDK requireNonNull (), и Guava checkNotNull () могут быть вызваны в любом месте кода с любым объектом. Например, вы можете вызвать их внутри цикла. requireNotNull () и checkNotNull () не могли предположить, что они вызываются с некоторыми аргументами метода и вызывают исключение IllegalArgumentException. Обратите внимание, что Guava также имеет Preconditions.checkArgument (), который генерирует исключение IllegalArgumentException.

Bogdan Calmac 08.03.2013 22:37

Справедливо замечание Богдана, хотя я подозреваю, что типичное (и обычно предполагаемое) использование requireNonNull - это проверка аргументов. Если бы вам нужно было проверить, что что-то не было нулевым в цикле, я бы подумал, что утверждение будет типичным способом.

MB. 08.08.2013 18:41

Я думаю, что JavaDoc Object.requireNonNull () добавляет веса аргументу: «Этот метод предназначен в первую очередь для проверки параметров в методах и конструкторах»

Richard Zschech 02.02.2015 17:42

Имейте в виду, что аргумент производительности в пользу неявных проверок на null часто неверен. JIT может распознавать пользовательские нулевые проверки и исключать следующую подразумеваемую нулевую проверку и / или использовать один и тот же набор оптимизаций для любого типа нулевой проверки. См. эта вики для получения дополнительной информации, в частности: Написанные пользователем нулевые проверки в большинстве случаев функционально идентичны проверкам, вставленным JVM. Конечно, если вы делаете что-то более интересное в своем null, например, пользовательские сообщения, эта оптимизация может не применяться.

BeeOnRope 03.11.2016 02:17

дихотомия ... Они не пересекаются? Только неперекрывающиеся части целого могут составлять дихотомию. Как я это вижу:

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

Это удвоит накладные расходы на создание исключений и не поможет, так как перехват NullPointerException ничего не даст. Единственное, что может помочь, это IllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException, но у нас нет множественного наследования.

maaartinus 30.01.2014 15:31

Я считаю, что более конкретные исключения должны быть заключены в более общие исключения. NPE - это выражение, а IAE - метод. Поскольку методы содержат операторы, содержащие выражения, IAE является более общим.

Sgene9 26.01.2018 19:46

Что касается накладных расходов, я понятия не имею. Но поскольку трассировки стека будут в основном идентичными, за исключением того, что имя исключения изменилось посередине, я думаю, что для двойных исключений не должно быть слишком много накладных расходов. Если кто-то беспокоится о накладных расходах, он может использовать операторы «if» для возврата значения null или -1 вместо генерации исключения.

Sgene9 26.01.2018 19:53

Некоторые коллекции предполагают, что null отклоняется с использованием NullPointerException, а не IllegalArgumentException. Например, если вы сравните набор, содержащий null, с набором, который отвергает null, первый набор вызовет containsAll на другом и перехватит его NullPointerException, но не IllegalArgumentException. (Я смотрю на реализацию AbstractSet.equals.)

Вы могли бы разумно утверждать, что использование непроверенных исключений таким образом является антипаттерном, что сравнение коллекций, содержащих null, с коллекциями, которые не могут содержать null, является вероятной ошибкой, которая действительно должен создает исключение, или что помещение null в коллекцию вообще является плохая идея. Тем не менее, если вы не хотите сказать, что equals должен генерировать исключение в таком случае, вы застряли, помня, что NullPointerException требуется в одних обстоятельствах, но не в других. ("IAE до NPE, кроме" c "...")

Я не понимаю, как содержит NPE в зависимости от элемента внутри коллекции. Единственная причина, по которой генерируется NPE (afaict), - это если сама коллекция имеет значение null (в этом случае NPE выбрасывается, потому что он пытается получить доступ к своему итератору). Однако это вызывает вопрос, следует ли проверять нулевой ввод или он должен распространяться до тех пор, пока не попытается получить доступ.

Alowaniak 08.07.2016 15:27

new TreeSet<>().containsAll(Arrays.asList((Object) null)); выбрасывает NPE, потому что List содержит null.

Chris Povirk 08.07.2016 17:13

Ах, действительно, TreeSet # содержит бросает NPE, «если указанный элемент равен нулю, и этот набор использует естественный порядок, или его компаратор не разрешает нулевые элементы». Посмотрел только на AbstractSet, который разрешает null, мой плохой. Лично мне кажется странным, что он не просто возвращает false, поскольку в этом случае невозможно добавить null.

Alowaniak 12.07.2016 18:16

Создание исключения, которое является эксклюзивным для аргументов null (будь то NullPointerException или пользовательский тип), делает автоматическое тестирование null более надежным. Это автоматическое тестирование может быть выполнено с отражением и набором значений по умолчанию, как в NullPointerTesterГуава. Например, NullPointerTester попытается вызвать следующий метод ...

Foo(String string, List<?> list) {
  checkArgument(string.length() > 0);
  // missing null check for list!
  this.string = string;
  this.list = list;
}

... с двумя списками аргументов: "", null и null, ImmutableList.of(). Он проверит, что каждый из этих вызовов вызывает ожидаемый NullPointerException. Для этой реализации при передаче списка nullнет создает NullPointerException. Однако случается, что получается IllegalArgumentException, потому что NullPointerTester использует строку по умолчанию "". Если NullPointerTester ожидает только NullPointerException для значений null, он обнаруживает ошибку. Если он ожидает IllegalArgumentException, он его пропускает.

На самом деле, вопрос создания исключения IllegalArgumentException или NullPointerException, на мой скромный взгляд, является лишь «священной войной» для меньшинства с неполным пониманием обработки исключений в Java. В целом правила просты и заключаются в следующем:

  • нарушения ограничений аргументов должны указываться как можно быстрее (-> быстрый сбой), чтобы избежать недопустимых состояний, которые намного сложнее отлаживать
  • в случае недопустимого нулевого указателя по какой-либо причине бросить NullPointerException
  • в случае недопустимого индекса массива / коллекции бросить ArrayIndexOutOfBounds
  • в случае отрицательного размера массива / коллекции бросить NegativeArraySizeException
  • в случае недопустимого аргумента, который не описан выше и для которого у вас нет другого более конкретного типа исключения, выбросить IllegalArgumentException как корзину для мусора
  • с другой стороны, в случае нарушения ограничения ВНУТРИ ПОЛЯ, которого не удалось избежать с помощью быстрого сбоя по какой-либо уважительной причине, перехватить и повторно выбросить как IllegalStateException или более конкретное проверенное исключение. Никогда не позволяйте передавать исходное исключение NullPointerException, ArrayIndexOutOfBounds и т. д. В этом случае!

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

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

(2) Отображение исключений на самом деле приводит к другому типу аномалии, вызванной единичным наследованием: все исключения Java являются классами и поэтому поддерживают только одиночное наследование. Следовательно, нет способа создать исключение, которое действительно является как NullPointerException, так и IllegalArgumentException, поскольку подклассы могут наследовать только от одного или другого. Таким образом, выброс IllegalArgumentException в случае нулевого аргумента затрудняет пользователям API различать проблемы всякий раз, когда программа пытается программно исправить проблему, например, вводя значения по умолчанию в повторение вызова!

(3) Сопоставление фактически создает опасность маскировки ошибки: чтобы сопоставить нарушения ограничений аргументов с IllegalArgumentException, вам необходимо закодировать внешний try-catch внутри каждого метода, который имеет какие-либо ограниченные аргументы. Однако о простом перехвате RuntimeException в этом блоке catch не может быть и речи, потому что это сопряжено с риском сопоставления задокументированных RuntimeExceptions, созданных вашими методами libery, с IllegalArgumentException, даже если они не вызваны нарушениями ограничений аргументов. Таким образом, вам нужно быть очень конкретным, но даже это усилие не защищает вас от случая, когда вы случайно сопоставляете недокументированное исключение времени выполнения другого API (то есть ошибку) в IllegalArgumentException вашего API. Таким образом, даже самое тщательное сопоставление рискует замаскировать ошибки программирования других создателей библиотек как нарушения ограничений аргументов пользователей вашего метода, что является просто чудовищным поведением!

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

Согласно вашему сценарию, IllegalArgumentException - лучший выбор, потому что null не является допустимым значением для вашей собственности.

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

IllegalArgumentException выдается, когда метод получает аргумент, отформатированный иначе, чем ожидает метод.

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

Это субъективный вопрос, который следует закрыть, но поскольку он все еще открыт:

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

NullPointerException: не бросать намеренно. NPE должны выдаваться только виртуальной машиной при разыменовании нулевой ссылки. Необходимо приложить все возможные усилия, чтобы их никогда не бросили. @Nullable и @NotNull следует использовать вместе с инструментами анализа кода, чтобы найти эти ошибки.

IllegalArgumentException: выбрасывается, когда аргумент функции не соответствует общедоступной документации, так что ошибка может быть идентифицирована и описана в терминах переданных аргументов. Ситуация OP попадает в эту категорию.

IllegalStateException: выбрасывается, когда функция вызывается, и ее аргументы либо являются неожиданными в момент их передачи, либо несовместимы с состоянием объекта, членом которого является метод.

Например, в вещах, имеющих длину, использовались две внутренние версии исключения IndexOutOfBoundsException. Один подкласс IllegalStateException, используемый, если индекс был больше длины. Другой подкласс IllegalArgumentException, используемый, если индекс был отрицательным. Это произошло потому, что вы могли добавить к объекту больше элементов, и аргумент был бы действительным, в то время как отрицательное число никогда не было бы допустимым.

Как я уже сказал, эта система работает очень хорошо, и потребовался кто-то, чтобы объяснить, почему существует различие: «В зависимости от типа ошибки вам довольно просто понять, что делать. Даже если вы не можете понять, Выяснив, что пошло не так, вы можете выяснить, где найти эту ошибку и создать дополнительную отладочную информацию ».

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

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

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

В любом случае, его точка зрения заключалась в том, что вы можете копировать только IllegalArgumentAssertions вверх по стеку. Вы не можете распространить исключения IllegalStateExceptions или NullPointerExceptions вверх по стеку, потому что они как-то связаны с вашей функцией.

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