IllegalArgumentException или NullPointerException для нулевого параметра? [закрытый]


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

26 494

26 ответов:

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

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

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

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

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

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

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

стандарт должен бросить исключение NullPointerException. Обычно непогрешимая "эффективная Java" кратко обсуждает это в пункте 42 (первое издание), пункте 60 (второе издание) или пункте 72 (третье издание) "выступает за использование стандартных исключений":

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

Я все за то, чтобы бросить 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 говоря, что было null и почему он не должен быть null. Так же, как и с IllegalArgumentException.

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

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

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

@бросает NullPointerException, если ключ равен null и эта карта не разрешает нулевые ключи (необязательно).

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

шаблон идет:

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

что бы вы ни делали, не позволяйте плохому значению быть установленным и бросать исключение позже, когда другой код пытается его использовать. Это делает отладку кошмаром. Вы всегда должны следовать принципу" fail-fast".

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

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

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

  • Я бы выбрал NPE over IAE по нескольким причинам

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

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

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

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

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

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

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

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

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

Мои 2 Цента

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

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

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

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

  • нарушения ограничений аргументов должны быть указаны как можно быстрее (- >fast fail), чтобы избежать незаконных состояний, которые намного сложнее отлаживать
  • в случае недопустимого указателя null для независимо от причины, бросьте 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 не может быть и речи, потому что это рискует сопоставить документированные RuntimeException, вызванные методами libery, используемыми в вашем, В IllegalArgumentException, даже если они не вызваны нарушениями ограничений аргументов. Поэтому вам нужно быть очень конкретным, но даже это усилие не защищает вас от случая, когда вы случайно сопоставляете недокументированное исключение времени выполнения другого API (т. е. ошибку) в IllegalArgumentException вашего ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС. Поэтому даже самое тщательное сопоставление рискует замаскировать ошибки программирования других производителей библиотек как нарушения ограничений аргументов пользователей вашего метода, что просто Хиллари поведение!

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

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

бросать исключение для null аргументов (будь то NullPointerException или пользовательский тип) делает automated null тестирование более надежным. Это автоматическое тестирование может быть сделано с отражением и набором значений по умолчанию, как в гуавы ' s 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, он скучает по нему.

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

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

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

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

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

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

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

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

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

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

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

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

throw new IllegalArgumentException(new NullPointerException(NULL_ARGUMENT_IN_METHOD_BAD_BOY_BAD));

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

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

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

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

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

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

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

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

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

Если это "сеттер", или где-то я получаю член для использования позже, я, как правило, используют IllegalArgumentException.

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

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

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

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

GaryF и tweakt отбросили" эффективные Java " (которые я клянусь) ссылки, которые рекомендуют использовать NPE. И глядя на то, как строятся другие хорошие API, это лучший способ увидеть, как построить ваш ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС.

еще один хороший пример-посмотреть на Spring API. Например, орг.springframework.зернышки.Бобовые.instantiateClass (конструктор ctor, Object[] args) имеет Assert.notNull (ctor, "конструктор не должен быть null") строка. орг.springframework.утиль.Утверждать.метод notNull (Object object, String message) проверяет, является ли переданный аргумент (object) нулевым, и если это так, он создает новое IllegalArgumentException (message), которое затем поймано в орг.springframework.зернышки.Бобовые.instantiateClass(...) метод.

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