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

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

Ответов (25)

Решение

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

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

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

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

Я был полностью за то, 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 вы получите автоматически при вызове метода на null. параметр. При условии, что вы вызываете метод быстро (т.е. быстро завершаете работу), вы получаете, по сути, тот же эффект, только не такой удобный для разработчика. В большинстве случаев, вероятно, лучше явно проверить и выдать полезное сообщение, чтобы указать, какой параметр был нулевым, но неплохо иметь возможность изменить это, если производительность диктует, не нарушая опубликованный контракт метода / конструктора.

Я хотел выделить нулевые аргументы из других недопустимых аргументов, поэтому я получил исключение из IAE с именем NullArgumentException. Даже не имея необходимости читать сообщение об исключении, я знаю, что в метод был передан нулевой аргумент, и, читая сообщение, я выясняю, какой аргумент был нулевым. Я все еще ловлю исключение 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 "...")

Создание исключения, которое является исключительным для 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Например, в Javadoc for Map.containsKey говорится:

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

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

Схема идет:

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

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

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

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

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

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

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

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

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

Мои 2 цента

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

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

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

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

Вы должны использовать 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 не является достаточно хорошим аргументом, чтобы превзойти эти другие соображения.

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

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

В этом случае 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 (...) метод.

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

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

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

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

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

  • Я бы выбрал NPE над МИА по нескольким причинам

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

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

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

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