Библиотека предварительных условий для выброса исключения IllegalArgumentException для проверки notNull

Знаете ли вы какую-нибудь хорошую альтернативу Apache Commons Validate или Предварительные условия Guava, которые будет генерировать исключение IllegalArgumentException вместо NullPointerException при проверке, является ли объект не нулевым (кроме Spring Assert)?


Мне известно, что Javadocs скажите:

Приложения должны генерировать экземпляры этого класса [NullPointerException], чтобы указать на другое незаконное использование нулевого объекта.

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

Скажем, у меня есть фасоль:

public class Person {
  private String name;
  private String phone;
  //....
}

и способ обслуживания:

public void call(Person person) {
  //assert person.getPhone() != null
  //....
}

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

Кроме того, уже были обсуждения, и есть этот хороший ответ, который я полностью поддерживаю. Итак, мой вопрос - нужно ли мне делать такие уродливые вещи:

Validate.isTrue(person.getPhone() != null, "Can't call a person that hasn't got a phone");

чтобы все было по-своему, или есть библиотека, которая просто выбрасывает IllegalArgumentException для проверки notNull?


person macias    schedule 04.05.2015    source источник
comment
Непонятно, что вы подразумеваете под правильно обработанной ситуацией. Конечно, это не случайное разыменование null, но оно сводится к тому же: метод вызывается с null, переданным для параметра, который не может быть null, что в равной степени является ошибкой программиста.   -  person ColinD    schedule 04.05.2015
comment
конечно, но в этом случае NPE (в моей интерпретации) обрабатывается, исключение IllegalArgumentException не обрабатывается. И на самом деле это обычно делается каким-то обработчиком исключений на уровне сервлета или чем-то еще.   -  person macias    schedule 04.05.2015
comment
FWIW, JDK устанавливает довольно твердый прецедент того, как он ожидает обработки недопустимых значений NULL.   -  person Louis Wasserman    schedule 04.05.2015
comment
Тем не менее, я, наверное, хотел бы найти библиотеку, которая противоречит этим ожиданиям :)   -  person macias    schedule 04.05.2015
comment
К сожалению, похоже, что Guava в ближайшее время не изменит NPE на IAE, ссылка. Я чувствую, что здесь вы приводите веские аргументы в пользу IAE, а не NPE.   -  person Captain Man    schedule 04.05.2015
comment
@ColinD - Я сдался и обновил вопрос относительно вашего комментария   -  person macias    schedule 04.05.2015
comment
для меня было бы довольно запутанным, если бы он был брошен там, где я ожидаю IllegalArgumentException. - А для людей, обслуживающих ваш код, будет довольно сложно получить IAE вместо NPE ... Просто используйте стандарты. Даже java.util.Objects.requireNotNull и друзья Кидай НПЕ.   -  person Olivier Grégoire    schedule 05.05.2015


Ответы (7)


Поскольку тема этого вопроса превратилась в «Правильное использование IllegalArgumentException и NullpointerException», я хотел бы указать на прямой ответ в Effective Java Item 60 (второе издание):

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

person Fritz Duchardt    schedule 11.05.2015

А как насчет Preconditions checkArgument?

public void call(Person person) {
    Preconditions.checkArgument(person.getPhone() != null);
    // cally things...
}

checkArgument throws IllegalArgumentException вместо NullPointerException.

person Captain Man    schedule 04.05.2015
comment
да, apache-commons Validate.isTrue также выбрасывает IllegalArgumentException, но Validate.notNull выбрасывает NPE, к моему разочарованию. - person macias; 04.05.2015
comment
Preconditions.checkArgument( ... != null); может быть не тем, что вы ищете, но он все равно читается немного лучше (на мой взгляд), чем Validate.isTrue( ... != null);, потому что ясно, что это связано с предварительными условиями и аргументами из имен. - person Captain Man; 04.05.2015
comment
ну да, наверное, лучше. +1;) - person macias; 04.05.2015
comment
@macias NullPointerException обычно рекомендуется вместо IllegalArgumentException для нулевых аргументов (см. Эффективная Java, пункт 60, как указано выше ). Это также довольно часто встречается в стандартных библиотеках, например в URI. create (). Это та же логика, что и бросание IndexOutOfBoundsException для аргумента за пределами допустимого диапазона в List.get () - более конкретно, и ошибка обычно возникает по определенным причинам. - person David Moles; 29.11.2017
comment
@DavidMoles для меня проблема в том, что NullPointerException не более конкретен, чем IllegalArgumentException. Для меня NPE означает, что с нулевым значением произошло что-то плохое, что кажется менее конкретным, чем был передан недопустимый аргумент. Вдобавок я думаю, что большинство людей сразу думают, что NPE - это разыменование нулевого указателя. - person Captain Man; 29.11.2017
comment
@CaptainMan используйте более конкретный Preconditions.checkNotNull (person.getPhone ()) вместо этого выдает NPE. - person Stefan L; 05.07.2019
comment
@StefanL Пожалуйста, перечитайте вопрос. альтернатива [...] предварительным условиям Guava, которые будут генерировать исключение IllegalArgumentException вместо NullPointerException при проверке, не является ли объект нулевым? Preconditions#checkNotNull выдает NullPointerException, если аргумент равен нулю, а не IllegalArgumentException. - person Captain Man; 08.07.2019

Вы можете использовать valid4j с hamcrest-matchers (найдены в Maven Central как org.valid4j: valid4j). Класс Validation поддерживает регулярную проверку ввода (т. Е. Выдачу восстанавливаемых исключений):

import static org.valid4j.Validation.*;

validate(argument, isValid(), otherwiseThrowing(InvalidException.class));

Ссылки:

На заметку: эта библиотека также поддерживает предварительные и пост-условия (например, утверждения), и при необходимости можно зарегистрировать свою собственную настраиваемую глобальную политику:

import static org.valid4j.Assertive.*;

require(x, greaterThan(0)); // throws RequireViolation extends AssertionError
...
ensure(r, notNullValue()); // throws EnsureViolation extends AssertionError
person keyoxy    schedule 19.01.2016

Взгляните на https://github.com/cowwoc/requirements.java/ ( Я автор). Вы можете переопределить тип исключения по умолчанию, используя withException() следующим образом:

new Verifiers().withException(IllegalArgumentException.class).requireThat(name, value).isNotNull();
person Gili    schedule 07.10.2016

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

class Preconditionz {
    public static <T> T checkNotNull(T reference, Object errorMessage) {
        if (reference == null) {
            throw new IllegalArgumentException(String.valueOf(errorMessage));
        }
        return reference;
    }
}

Мне нравится продолжать и import static эти действительно часто используемые методы, так что вы можете называть их очень кратко.

import static com.whatever.util.Preconditionz.checkNotNull;

// ...

public void call(Person person) {
    checkNotNull(person, "person");
    checkNotNull(person.getPhone(), "person.phone");
    // ...
}

В зависимости от вашей среды вы можете назвать его checkNotNull2, чтобы было проще добавить импорт через автозаполнение в вашу среду IDE или позволить вам использовать его вместе со стандартным checkNotNull.

person Andrew Janke    schedule 04.05.2015

Думаю, я снова кое-что узнал здесь, на SO, благодаря замечательным комментариям Оливье Грегуара, Луи Вассермана, CollinD и Captain Man. Стандарты обычно являются веской и достаточной причиной, поскольку они заставляют программистов на общеязыковых языках всегда понимать правильно, но в этом конкретном случае у меня было небольшое сомнение, что, возможно, это правило, установленное вокруг NPE, не слишком хорошо. Java - это старый язык, и некоторым его функциям не повезло (я не хочу ошибаться, возможно, это слишком строгое суждение) - например, отмечены исключения, хотя вы также можете не согласиться. Теперь я думаю, что это сомнение рассеялось и мне следует:

  • Вызывайте исключение IllegalArgumentException, когда в конкретном контексте я могу сказать, почему нулевое значение неверно, а не с точки зрения бизнеса. Например, в методе обслуживания public void call(Person person) я знаю, что означает для системы, что номер телефона равен нулю.
  • Вызывайте исключение NullPointerException, когда я просто знаю, что значение null здесь неверно и рано или поздно вызовет исключение NullPointerException, но в конкретном контексте я не знаю, что это означает с точки зрения бизнеса. Примером могут быть неизменяемые коллекции Guavas. Когда вы создаете такое и пытаетесь добавить элемент с нулевым значением, он выдает вам NPE. Он не понимает, что это значение означает для вас, это слишком общий характер, но он просто знает, что здесь неправильно, поэтому он решает также сказать вам это немедленно, с более подходящим сообщением, чтобы вы могли более эффективно распознать проблему.

Имея в виду вышеизложенное, я бы сказал, что лучший вариант сделать утверждение в примере public void call(Person person) - это то, что предлагает Капитан Человек:

Preconditions.checkArgument(person.getPhone() != null, "msg");

Проверить аргумент - хорошее название для этого метода - ясно, что я проверяю соответствие бизнес-контракта аргументу человека, и ясно, что я ожидаю исключения IllegalArgumentException в случае сбоя. Это имя лучше, чем Validate.isTrue из Apache Commons. С другой стороны, использование Validate.notNull или Preconditions.checkNotNull предполагает, что я проверяю нулевую ссылку и на самом деле ожидаю NPE.

Итак, окончательный ответ - такой красивой библиотеки не существует, и ее не должно быть, так как это сбивает с толку. (И Spring Assert надо поправить).

person macias    schedule 05.05.2015

Вы легко можете это сделать:

if (person.getPhone() == null) {
    throw new IllegalArgumentException("Can't call a person that hasn't got a phone");
}

Другим программистам понятно, что вы имеете в виду, и они делают именно то, что вы хотите.

person Chronio    schedule 04.05.2015
comment
да, это еще хуже :) Мой уродливый пример тоже делает то, что я не хочу, и это тоже ясно. Мой вопрос касался библиотеки, которая выдает исключение IllegalArgumentException вместо NPE (как это делают guava и apache-commons) для проверки notNull. - person macias; 04.05.2015
comment
Собственно, это нормальный способ проверки параметров в Java, не зависящий от какой-либо библиотеки. Насколько я могу судить, нет причин не использовать это. Кроме того, я не знаю ни одного руководства по стилю кодирования, в котором говорилось бы, что это некрасиво. - person Chronio; 04.05.2015
comment
Так сказано в моем руководстве по стилю;) Но, если серьезно, я тоже часто делаю то, что вы предлагаете. Мой вопрос немного о другом. Цель состоит в том, чтобы получить короткую аккуратную проверку, например Validate.notNull (person.getPhone (), msg); - person macias; 04.05.2015