Стиль конструктора 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");
}
//...
}
}


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



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

715   10  

10 ответов:

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

потому что он говорит пользователю вашего 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");
    ...
}

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

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

 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.


On IllegalArgumentException vs NullPointerException

в то время как ваш оригинальный код выдает IllegalArgumentException on null аргументы, гуавы Preconditions.checkNotNull закидываем NullPointerException вместо.

вот цитата из эффективное Java 2-е издание: пункт 60: пользу использование стандартных исключений:

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

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

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

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

добавлена 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 (если вы их отмените, ну, тогда ваша проверка никогда не подведет)
  • еще одна лучшая практика: если возможно, сделайте все члены вашего класса окончательными (так что вы можете быть уверены: когда какой-то объект был создан успешно, все его члены не являются нулевыми; и они не будут меняться с течением времени)

у меня был бы Служебный метод:

 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);
 }

EDIT: что касается предложений использовать стороннюю библиотеку, предварительные условия Google, в частности, делают выше даже лучше, чем мой код. Однако, если это единственная причина включить библиотеку в ваш проект, я бы колебался. Метод слишком прост.

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

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

сравнение способов проверить условия в Java - гуава и Апач Коммонс и весенние рамки и обычного Java-утверждает

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

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

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

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

кстати, я голосую за Вариант (1):

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

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

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

общественные SomeClass( @непустой объект один, @ненулевое два объекта) {

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

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

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

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

Я недавно начал использовать Google guava, и я считаю, что - наряду с Apache commons - Как только вы начнете использовать их, вы не будете использовать только для этого одного метода. Вы будете открывать и использовать его все больше и больше. В конце концов, это сделает ваш код короче, более читабельным, более последовательным и более доступным для обслуживания.

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

Comments

    Ничего не найдено.