Подтвердить что ты не робот

Стиль конструктора Java: параметры проверки не равны нулю

Каковы наилучшие методы, если у вас есть класс, который принимает некоторые параметры, но никому из них не разрешено быть null?

Ниже очевидно, но исключение немного неспецифично:

public class SomeClass
{
     public SomeClass(Object one, Object two)
     {
        if (one == null || two == null)
        {
            throw new IllegalArgumentException("Parameters can't be null");
        }
        //...
     }
}

Здесь исключения позволяют узнать, какой параметр имеет значение null, но конструктор теперь довольно уродлив:

public class SomeClass
{
     public SomeClass(Object one, Object two)
     {
        if (one == null)
        {
            throw new IllegalArgumentException("one can't be null");
        }           
        if (two == null)
        {
            throw new IllegalArgumentException("two can't be null");
        }
        //...
  }

Здесь конструктор более аккуратный, но теперь код конструктора на самом деле не находится в конструкторе:

public class SomeClass
{
     public SomeClass(Object one, Object two)
     {
        setOne(one);
        setTwo(two);
     }


     public void setOne(Object one)
     {
        if (one == null)
        {
            throw new IllegalArgumentException("one can't be null");
        }           
        //...
     }

     public void setTwo(Object two)
     {
        if (two == null)
        {
            throw new IllegalArgumentException("two can't be null");
        }
        //...
     }
  }

Какой из этих стилей лучше?

Или есть альтернатива, которая более широко принята?

4b9b3361

Ответ 1

Второй или третий.

Потому что он сообщает пользователю вашего API, что именно пошло не так.

За меньшее количество слов используйте Validate.notNull(obj, message) от commons-lang. Таким образом, ваш конструктор будет выглядеть так:

public SomeClass(Object one, Object two) {
    Validate.notNull(one, "one can't be null");
    Validate.notNull(two, "two can't be null");
    ...
}

Размещение проверки в сеттере также приемлемо, с тем же комментарием комментария. Если ваши сеттеры также играют роль сохранения согласованности объектов, вы также можете выбрать третью.

Ответ 2

Вы можете использовать одну из многих библиотек, предназначенную для облегчения проверки предварительных условий. Много кода в Google Guava использует com.google.common.base.Preconditions

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

 if (count <= 0) {
   throw new IllegalArgumentException("must be positive: " + count);
 }

чтобы заменить его более компактным

 checkArgument(count > 0, "must be positive: %s", count);

Он имеет checkNotNull, который широко используется в Guava. Затем вы можете написать:

 import static com.google.common.base.Preconditions.checkNotNull;
 //...

 public SomeClass(Object one, Object two) {
     this.one = checkNotNull(one);
     this.two = checkNotNull(two, "two can't be null!");
     //...
 }

Большинство методов перегружены, чтобы либо не принимать сообщение об ошибке, ни сообщение об ошибке, ни шаблонное сообщение об ошибке с varargs.


Вкл IllegalArgumentException vs NullPointerException

В то время как ваш исходный код вызывает IllegalArgumentException on null аргументы, вместо этого Guava Preconditions.checkNotNull выбрасывает NullPointerException.

Здесь цитата из Effective Java 2nd Edition: Пункт 60: Благодарите за использование стандартных исключений:

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

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

System.out.println("some string".split(null));
// throws NullPointerException

Ответ 3

Старый вопрос; еще один новый ответ (уже упомянутый другим комментарием, но я считаю, что стоит его собственный ответ).

Java 7 добавила java.lang.Objects.requireNonNull() к API, которые все могут использовать. Таким образом, проверка всех аргументов для null сводится к короткому списку, например:

this.arg1 = Objects.requireNonNull(arg1, "arg1 must not be null");
this.arg2 = Objects.requireNonNull(arg2, "arg2 must not be null");

Боковые заметки:

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

Ответ 4

У меня был бы метод утилиты:

 public static <T> T checkNull(String message, T object) {
     if(object == null) {
       throw new NullPointerException(message);
     }
     return object;
  }

Я хотел бы, чтобы он возвращал объект, чтобы вы могли использовать его в назначениях следующим образом:

 public Constructor(Object param) {
     this.param = checkNull("Param not allowed to be null", param);
 }

РЕДАКТОР. Что касается предложений об использовании сторонней библиотеки, предпосылки Google, в частности, делают это даже лучше, чем мой код. Однако, если это единственные причины для включения библиотеки в ваш проект, я бы колебался. Метод слишком прост.

Ответ 5

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

Из блога Misko Hevery по тестируемости: Утверждать или не утверждать

Ответ 6

Сравнение способов проверки предусловий в Java - Guava vs. Apache Commons vs. Spring Framework vs. Plain Java Asserts

public static void fooSpringFrameworkAssert(String name, int start, int end) {
        // Preconditions
        Assert.notNull(name, "Name must not be null");
        Assert.isTrue(start < end, "Start (" + start + ") must be smaller than end (" + end + ")");

        // Do something here ...
    }

    public static void fooApacheCommonsValidate(String name, int start, int end) {
        // Preconditions
        Validate.notNull(name, "Name must not be null");
        Validate.isTrue(start < end, "Start (%s) must be smaller than end (%s)", start, end);

        // Do something here ...
    }

    public static void fooGuavaPreconditions(String name, int start, int end) {
        // Preconditions
        Preconditions.checkNotNull(name, "Name must not be null");
        Preconditions.checkArgument(start < end, "Start (%s) must be smaller than end (%s)", start, end);

        // Do something here ...
    }

    public static void fooPlainJavaAsserts(String name, int start, int end) {
        // Preconditions
        assert null != name : "Name must not be null";
        assert start < end : "Start (" + start + ") must be smaller than end (" + end + ")";

        // Do something here ...
    }

Это резюме этой статьи: http://www.sw-engineering-candies.com/blog-1/comparison-of-ways-to-check-preconditions-in-java

Ответ 7

Альтернативой отбрасыванию неконтролируемого исключения будет использование assert. В противном случае я бы выбрал проверенные исключения, чтобы уведомить об этом факта, что конструктор не будет работать с недопустимыми значениями.

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

Обратите внимание, что второй и третий пример не могут правильно сообщать, что оба параметра имеют значение null.

Кстати - я голосую за вариант (1):

if (one == null || two == null) {
    throw new IllegalArgumentException(
      String.format("Parameters can't be null: one=%s, two=%s", one, two));
}

Ответ 8

Аннотации для статического анализа также полезны либо в дополнение, либо на месте проверок времени выполнения.

FindBugs, например, предоставляет аннотацию @NonNull.

public SomeClass (@NonNull Object one, @NonNull Object two) {

Ответ 9

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

Ответ 10

Я предполагаю, что вы говорите о встроенном assert в Java. По-моему, это не очень хорошая идея. Поскольку он может быть включен/выключен с использованием параметров командной строки. Поэтому некоторые говорят, что это приемлемо только для частных методов.

Мои наставники говорят мне не изобретать колесо! Их советы - использовать библиотеки. Они (вероятно) хорошо спроектированы и протестированы. Конечно, вы несете ответственность за то, чтобы вы захватили библиотеку хорошего качества.

Другие говорят мне, что Enterprise ppl - в некоторых терминах - ошибается, и вы вводите большую зависимость - для простых задач - чем требуется. Я тоже могу принять этот момент... Но вот мой последний опыт:

Сначала я написал свой собственный частный метод для проверки нулевых параметров. Это скучно и скучно. Я знаю, что я должен поместить его в класс Utility. Но почему я должен писать это на первом месте, когда кто-то уже это сделал? Я могу сэкономить время, не записывая unit test и не создавая существующий материал. Если вы не хотите тренироваться или учиться, я бы не рекомендовал это делать.

Недавно я начал использовать google guava, и я обнаружил, что вместе с apache commons - как только вы начнете их использовать, вы не будете использовать только один единственный метод. Вы будете открывать и использовать его все больше и больше. В конце это сделает ваш код более коротким, читабельным, более последовательным и более удобным.

Кстати: В зависимости от ваших целей я бы пошел с 2 или 3, используя одну из вышеупомянутых библиотек выше...