Стиль конструктора 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");
}
//...
}
}
что из этих стилей лучше?
или есть альтернатива, которая более широко принята?
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
IllegalArgumentExceptionvsNullPointerExceptionв то время как ваш оригинальный код выдает
IllegalArgumentExceptiononnullаргументы, гуавы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