У меня есть простой метод установки для свойства и null
не подходит для этого конкретного свойства. Я всегда разрывался в этой ситуации: я должен бросить IllegalArgumentException
, или NullPointerException
? От javadocs оба кажутся подходящими. Есть ли какой-то понятый стандарт? Или это всего лишь одна из тех вещей, которые вы должны делать, что хотите, и оба они действительно верны?
Кажется, что IllegalArgumentException
вызывается для, если вы не хотите null
быть допустимым значением, и NullPointerException
будет выброшено, если вы пытаетесь использовать переменную, которая оказывается null
.
Если это setter
метод и null
передается ему, я думаю, что было бы больше смысла бросить IllegalArgumentException
. NullPointerException
Кажется, A имеет больше смысла в том случае, когда вы пытаетесь использовать null
.
Таким образом, если вы используете его , и это null
, NullPointer
. Если он передается в и это null
, IllegalArgument
.
Я склонен следить за дизайном библиотек JDK, особенно коллекций и параллелизма (Джошуа Блох, Даг Ли, эти ребята знают, как создавать надежные API). Во всяком случае, многие API в JDK активно выбрасывают NullPointerException
.
Например, Javadoc для Map.containsKey
государств:
@throws NullPointerException, если ключ является нулевым, и эта карта не допускает нулевые ключи (необязательно).
Это совершенно справедливо, чтобы бросить свой собственный NPE. Соглашение состоит в том, чтобы включить имя параметра, который был нулевым, в сообщении об исключении.
Шаблон идет:
public void someMethod(Object mustNotBeNull) {
if (mustNotBeNull == null) {
throw new NullPointerException("mustNotBeNull must not be null");
}
}
Что бы вы ни делали, не допускайте установки плохого значения и генерируйте исключение позже, когда другой код пытается его использовать. Это делает отладку кошмаром. Вы всегда должны следовать принципу «быстро-неудачно».
Если вы решите бросить NPE и используете аргумент в своем методе, это может быть излишним и дорогостоящим для явной проверки на нулевое значение. Я думаю, что виртуальная машина уже делает это для вас.
NPE
а программисты говорят IAE
перед ВМ, если они хотят. Общепринятая практика, если использовать IllegalArgumentException (String message), чтобы объявить параметр недействительным и дать как можно больше подробностей ... Так что сказать, что параметры были определены как нулевые, а исключение ненулевое, вы бы что-то сделали как это:
if( variable == null )
throw new IllegalArgumentException("The object 'variable' cannot be null");
У вас практически нет причин неявно использовать «NullPointerException». NullPointerException - это исключение, которое выдается виртуальной машиной Java при попытке выполнить код по нулевой ссылке (например, toString () ).
Стандарт бросить NullPointerException
. Обычно безошибочная «Эффективная Java» кратко обсуждает это в пункте 42 (первое издание), пункте 60 (второе издание) или пункте 72 (третье издание) «Пользу от использования стандартных исключений»:
«Возможно, все ошибочные вызовы методов сводятся к недопустимому аргументу или недопустимому состоянию, но другие исключения стандартно используются для определенных типов недопустимых аргументов и состояний. Если вызывающая сторона передает нуль в некотором параметре, для которого нулевые значения запрещены, соглашение предписывает, что Исключение NullPointerException, а не IllegalArgumentException. "
Определения из ссылок на два вышеупомянутых исключения являются IllegalArgumentException: Брошенный, чтобы указать, что методу был передан недопустимый или неподходящий аргумент. NullPointerException: выдается, когда приложение пытается использовать значение NULL в случае, когда требуется объект.
Большая разница здесь заключается в том, что IllegalArgumentException предполагается использовать при проверке правильности аргумента метода. Предполагается, что NullPointerException будет использоваться всякий раз, когда объект «используется», когда он нулевой.
Я надеюсь, что это помогает поставить два в перспективе.
Не могу согласиться с тем, что говорится. Неудача рано, неудача быстро. Довольно хорошая Исключительная мантра.
Вопрос о том, какое исключение бросить, в основном вопрос личного вкуса. На мой взгляд, IllegalArgumentException кажется более конкретным, чем использование NPE, так как он говорит мне, что проблема была в аргументе, который я передал методу, а не в значении, которое могло быть сгенерировано при выполнении метода.
Мои 2 цента
Если это «сеттер» или где-то, что я получаю, чтобы член использовал позже, я склонен использовать IllegalArgumentException.
Если это то, что я собираюсь использовать (разыменование) прямо сейчас в методе, я предварительно выбрасываю исключение NullPointerException. Мне нравится это лучше, чем позволить среде выполнения делать это, потому что я могу предоставить полезное сообщение (кажется, что среда выполнения может сделать это тоже, но это напыщенная речь на другой день).
Если я переопределяю метод, я использую то, что использует переопределенный метод.
В общем, разработчик никогда не должен выбрасывать исключение NullPointerException. Это исключение выдается средой выполнения, когда код пытается разыменовать переменную, значение которой равно нулю. Следовательно, если ваш метод хочет явно запретить значение NULL, в отличие от того, что значение NULL приводит к возникновению исключения NullPointerException, вы должны вызвать исключение IllegalArgumentException.
Вы должны использовать IllegalArgumentException
(IAE), а не NullPointerException
(NPE) по следующим причинам:
Во-первых, NPD JavaDoc явно перечисляет случаи, когда NPE подходит. Обратите внимание, что все они выбрасываются средой выполнения, когда null
используется не по назначению. Напротив, JavaDoc IAE не может быть более ясным: «Брошенный, чтобы указать, что метод был передан недопустимый или неподходящий аргумент». Да, это ты!
Во-вторых, когда вы видите NPE в трассировке стека, что вы предполагаете? Вероятно, что кто-то разыменовал null
. Когда вы видите IAE, вы предполагаете, что вызывающий метод в верхней части стека передан в недопустимом значении. Опять же, последнее предположение верно, первое вводит в заблуждение.
В-третьих, поскольку IAE явно предназначен для проверки параметров, вы должны принять его за выбор исключений по умолчанию, так почему бы вместо этого выбрать NPE? Конечно, не для другого поведения - вы действительно ожидаете, что вызывающий код будет перехватывать NPE отдельно от IAE и в результате будет делать что-то другое? Вы пытаетесь сообщить более конкретное сообщение об ошибке? Но вы все равно можете сделать это в тексте сообщения об исключении, как и для всех других неверных параметров.
В-четвертых, все другие неверные данные параметров будут IAE, так почему бы не быть последовательными? Почему незаконность null
настолько особенная, что она заслуживает отдельного исключения из всех других типов незаконных аргументов?
Наконец, я принимаю аргумент, приведенный в других ответах, что части API Java используют NPE таким образом. Однако API Java несовместим со всем, от типов исключений до соглашений об именах, поэтому я думаю, что просто слепое копирование (ваша любимая часть) Java API не является достаточным аргументом, чтобы превзойти эти другие соображения.
Validate.notNull
(commons lang) и Preconditions.checkNotNull
(guava) оба бросают NPE :-(checkArgument(arg != null)
, просто без удобства возврата arg, или вы можете создать локальная утилита для вашего проекта. " code.google.com/p/guava-libraries/wiki/IdeaGraveyardВы должны выдать исключение IllegalArgumentException, так как для программиста станет очевидным, что он сделал что-то недопустимое. Разработчики настолько привыкли видеть NPE, выбрасываемый виртуальной машиной, что любой программист не сразу осознает свою ошибку и начнет оглядываться по сторонам или, что еще хуже, обвинять ваш код в «глючении».
Это вопрос стиля "Священной войны". Другими словами, обе альтернативы хороши, но у людей будут свои предпочтения, которые они будут защищать до смерти.
NullPointerException
должно быть выброшено: это соглашение, которое использует JDK, и требует интерфейсов, оно более конкретное (как и IndexOutOfBoundsException
т. Д.) И т. Д.В этом случае IllegalArgumentException передает ясную информацию пользователю, используя ваш API, что «не должно быть нулевым». Как отмечали другие пользователи форума, вы можете использовать NPE, если хотите, если вы предоставляете нужную информацию пользователю, используя ваш API.
GaryF и tweakt удалили ссылки на «Эффективную Java» (которые, клянусь), которые рекомендуют использовать NPE. И, глядя на то, как создаются другие хорошие API, это лучший способ увидеть, как создать ваш API.
Еще один хороший пример - взглянуть на API Spring. Например, org.springframework.beans.BeanUtils.instantiateClass (конструктор ctor, Object [] args) имеет строку Assert.notNull (ctor, «Конструктор не должен быть нулевым»). Метод org.springframework.util.Assert.notNull (Object object, String message) проверяет, является ли переданный аргумент (объект) нулевым, и если это так, генерирует новое исключение IllegalArgumentException (message), которое затем перехватывается в орг. springframework.beans.BeanUtils.instantiateClass (...) метод.
Я хотел выделить аргументы Null из других недопустимых аргументов, поэтому я вывел исключение из IAE с именем NullArgumentException. Даже не имея необходимости читать сообщение об исключении, я знаю, что в метод был передан нулевой аргумент, и, прочитав сообщение, я выясняю, какой аргумент был нулевым. Я все еще ловлю исключение NullArgumentException с помощью обработчика IAE, но в моих журналах я могу быстро увидеть разницу.
Apache Commons Lang имеет исключение NullArgumentException, которое выполняет ряд вещей, обсуждаемых здесь: оно расширяет IllegalArgumentException, а его единственный конструктор принимает имя аргумента, который должен быть ненулевым.
Хотя я чувствую, что создание чего-то вроде NullArgumentException или IllegalArgumentException более точно описывает исключительные обстоятельства, мои коллеги и я предпочли подчиниться совету Блоха по этому вопросу.
Проголосовал за аргумент Джейсона Коэна, потому что он был хорошо представлен. Позвольте мне расчленить это шаг за шагом. ;-)
В JavaDoc NPE явно сказано: «другие незаконные использования нулевого объекта» . Если бы он был ограничен ситуациями, когда среда выполнения встречает ноль, а не должен, все такие случаи можно определить гораздо более кратко.
Ничего не поделаешь, если вы предполагаете, что что-то не так, но при условии, что инкапсуляция применяется должным образом, вам действительно не нужно заботиться или замечать, была ли ненулевая ссылка разыменована ненадлежащим образом по сравнению с тем, обнаружил ли метод несоответствующий нуль и вызвал исключение.
Я бы выбрал NPE над МИА по нескольким причинам
- Более конкретно о характере незаконной операции
- Логика, которая по ошибке допускает нулевые значения, имеет тенденцию сильно отличаться от логики, которая по ошибке допускает недопустимые значения. Например, если я проверяю данные, введенные пользователем, если получаю недопустимое значение, источником этой ошибки является конечный пользователь приложения. Если я получаю ноль, это ошибка программиста.
- Неверные значения могут вызывать такие вещи, как переполнение стека, ошибки нехватки памяти, синтаксический анализ исключений и т. Д. Действительно, большинство ошибок в какой-то момент обычно представляют собой недопустимое значение при вызове некоторых методов. По этой причине я вижу IAE как НАИБОЛЕЕ ОБЩЕЕ из всех исключений в RuntimeException.
На самом деле, другие недопустимые аргументы могут привести к разным исключениям. UnknownHostException , FileNotFoundException , различные исключения синтаксических ошибок, IndexOutOfBoundsException , ошибки аутентификации и т. Д., И т. Д.
В целом, я чувствую, что NPE сильно порочен, потому что традиционно ассоциируется с кодом, который не соответствует принципу fast fast . Это, плюс неспособность JDK заполнить NPE строкой сообщения, действительно породила сильное негативное мнение, которое не вполне обосновано. На самом деле, разница между NPE и IAE с точки зрения времени выполнения - это просто название. С этой точки зрения, чем точнее вы указали имя, тем больше ясности вы дадите звонящему.
Я был за то, чтобы выбрасывать 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
является то, что в коде с высокой производительностью вы можете обойтись без явной проверки нуля (и NullPointerException
с дружественным сообщением об ошибке), и просто полагаться на то, что NullPointerException
вы получите автоматически при вызове метода для нуля параметр. При условии, что вы вызываете метод быстро (т. Е. Быстро терпите неудачу), вы получаете тот же эффект, но не совсем удобный для разработчика. В большинстве случаев, вероятно, лучше явно проверить и выдать полезное сообщение, чтобы указать, какой параметр был нулевым, но хорошо иметь возможность изменить это, если производительность диктует, не нарушая опубликованный контракт метода / конструктора.
Preconditions.checkNotNull(arg)
также бросает NPE. дихотомия ... они не перекрываются? Только непересекающиеся части целого могут создавать дихотомию. Как я вижу это:
throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));
NullPointerException
ничего не даст. Единственное, что может помочь IllegalNullPointerArgumentException extends IllegalArgumentException, NullPointerException
, но у нас нет множественного наследования. Некоторые коллекции предполагают, что null
отклонено использование NullPointerException
вместо IllegalArgumentException
. Например, если вы сравните набор, содержащий null
набор, который отклоняет null
, первый набор вызовет containsAll
другой и поймает его NullPointerException
- но не IllegalArgumentException
. (Я смотрю на реализацию AbstractSet.equals
.)
Вы могли бы обоснованно утверждать, что использование непроверенных исключений таким способом является антипаттерном, что сравнение коллекций, которые содержат, null
с коллекциями, которые не могут содержать, null
является вероятной ошибкой, которая действительно должна вызвать исключение, или что null
добавление коллекции вообще является плохой идеей. , Тем не менее, если вы не готовы сказать, что это equals
должно вызвать исключение в таком случае, вы застряли, вспомнив, что NullPointerException
требуется в определенных обстоятельствах, но не в других. («IAE до NPE, кроме как после« c »...»)
new TreeSet<>().containsAll(Arrays.asList((Object) null));
бросает, NPE
потому что List
содержит null
. Создание исключения, исключающего 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. В общем, правила просты и таковы:
- Нарушения ограничения аргумента должны указываться как можно быстрее (-> fast fail), чтобы избежать недопустимых состояний, которые намного сложнее отлаживать
- в случае недействительного нулевого указателя по какой-либо причине, сгенерируйте исключение NullPointerException
- в случае недопустимого индекса массива / коллекции, бросить ArrayIndexOutOfBounds
- в случае отрицательного размера массива / коллекции, сгенерируйте исключение NegativeArraySizeException
- в случае недопустимого аргумента, который не описан выше, и для которого у вас нет другого более конкретного типа исключения, выведите IllegalArgumentException в качестве корзины для мусора
- с другой стороны, в случае нарушения ограничения WITHIN A FIELD, которого нельзя было избежать быстрым отказом по какой-либо уважительной причине, перехватите и перебросьте как IllegalStateException или более конкретное проверенное исключение. Никогда не пропускайте исходное исключение NullPointerException, ArrayIndexOutOfBounds и т. Д. В этом случае!
Есть по крайней мере три веские причины против случая сопоставления всех видов нарушений ограничения аргументов с IllegalArgumentException, причем третий, вероятно, настолько серьезен, что обозначает плохой стиль практики:
(1) Программист не может с уверенностью предположить, что все случаи нарушения ограничения аргумента приводят к IllegalArgumentException, потому что подавляющее большинство стандартных классов используют это исключение скорее как корзину для мусора, если нет более конкретного доступного исключения. Попытка отобразить все случаи нарушений ограничения аргументов в IllegalArgumentException в вашем API только приводит к разочарованию программиста при использовании ваших классов, так как стандартные библиотеки в основном следуют различным правилам, которые нарушают ваши, и большинство ваших пользователей API также будут их использовать!
(2) Отображение исключений фактически приводит к другому типу аномалии, вызванной одиночным наследованием: все исключения Java являются классами и поэтому поддерживают только одиночное наследование. Следовательно, нет никакого способа создать исключение, которое действительно скажет и NullPointerException, и IllegalArgumentException, поскольку подклассы могут наследоваться только от одного или другого. Поэтому создание исключения IllegalArgumentException в случае нулевого аргумента усложняет пользователям API различие между проблемами, когда программа пытается программно исправить проблему, например, путем ввода значений по умолчанию в повтор вызова!
(3) Отображение фактически создает опасность маскирования ошибок: для того, чтобы отобразить нарушения ограничения аргументов в IllegalArgumentException, вам необходимо кодировать внешний try-catch внутри каждого метода, который имеет какие-либо ограниченные аргументы. Однако просто перехватить исключение RuntimeException в этом блоке перехвата не может быть и речи, потому что это рискует отобразить документированные исключения RuntimeException, созданные вашими свободными методами, в 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 вверх по стеку, поскольку они имеют какое-то отношение к вашей функции.
IllegalArgumentException
находится в противоречии с Java Objects.requireNonNull (T) и Гуавой Preconditions.checkNotNull (T), которая выбрасываетNullPointerException
. Тем не менее, правильный ответ определенноIllegalArgumentException
описан в превосходном ответе Джейсона Коэна и в разделе комментариев .