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

StackOverflow https://stackoverflow.com/questions/3881

Вопрос

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

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

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

Я склонен следить за дизайном библиотек 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 JavaDoc прямо говорит, «другое незаконное использование нулевого объекта».Если бы это ограничивалось только ситуациями, когда среда выполнения встречает нулевое значение, хотя это не должно быть, все такие случаи можно было бы определить гораздо более лаконично.

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

  • я бы выбрал НПЭ над ИАЭ по нескольким причинам

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

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

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

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

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

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

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

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

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

Мои 2 цента

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

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

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

На самом деле, вопрос о выбрасывании 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 не может быть и речи, потому что это рискует отобразить документированные исключения RuntimeException, созданные методами libery, используемыми внутри вас, в IllegalArgumentException, даже если они не вызваны нарушениями ограничений аргументов.Поэтому вам нужно быть очень конкретным, но даже эти усилия не защитят вас от случая, когда вы случайно сопоставите недокументированное исключение времени выполнения другого API (т.ошибка) в исключение IllegalArgumentException вашего API.Таким образом, даже самое тщательное сопоставление рискует замаскировать ошибки программирования других создателей библиотек как нарушения ограничений аргументов пользователей вашего метода, а это просто возмутительное поведение!

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

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

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

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

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

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

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

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

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

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

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

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

Я хотел выделить аргументы 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'...»)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top