Вопрос-ответ

How do I avoid checking for nulls in Java?

Как мне избежать проверки на наличие нулей в Java?

Я используюx != null, чтобы избежать NullPointerException. Есть ли альтернатива?

if (x != null) {
// ...
}
Переведено автоматически
Ответ 1

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

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


  1. Где null - допустимый ответ с точки зрения контракта; и



  2. Где это недопустимый ответ.



(2) - это просто. Начиная с Java 1.7, вы можете использовать Objects.requireNonNull(foo). (Если вы застряли на предыдущей версии, то assertions может быть хорошей альтернативой.)

"Правильное" использование этого метода было бы таким, как показано ниже. Метод возвращает переданный в него объект и выдает NullPointerException если объект равен null. Это означает, что возвращаемое значение всегда ненулевое. Метод в первую очередь предназначен для проверки параметров.

public Foo(Bar bar) {
this.bar = Objects.requireNonNull(bar);
}

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

Objects.requireNonNull(someobject, "if someobject is null then something is wrong");
someobject.doCalc();

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

(1) немного сложнее. Если у вас нет контроля над вызываемым кодом, вы застряли. Если null является допустимым ответом, вы должны проверить его.

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

С не-коллекциями это может быть сложнее. Рассмотрим это как пример: если у вас есть эти интерфейсы:

public interface Action {
void doSomething();
}

public interface Parser {
Action findAction(String userInput);
}

где анализатор принимает необработанный пользовательский ввод и находит, что нужно сделать, возможно, если вы для чего-то реализуете интерфейс командной строки. Теперь вы могли бы заключить контракт, согласно которому он возвращает null, если нет соответствующего действия. Это приводит к проверке null, о которой вы говорите.

Альтернативное решение - никогда не возвращать null и вместо этого использовать шаблон Null Object:

public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};

public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}

Сравнить:

Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}

Для

ParserFactory.getParser().findAction(someInput).doSomething();

это гораздо лучший дизайн, потому что он приводит к более сжатому коду.

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

try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}

Или, если вы считаете, что механизм try / catch слишком уродлив, вместо того, чтобы ничего не делать, ваше действие по умолчанию должно обеспечивать обратную связь с пользователем.

public Action findAction(final String userInput) {
/* Code to return requested Action if found */
return new Action() {
public void doSomething() {
userConsole.err("Action not found: " + userInput);
}
}
}
Ответ 2

Если вы используете (или планируете использовать) Java IDE, такую как JetBrains IntelliJ IDEA, Eclipse или Netbeans, или такой инструмент, как findbugs, вы можете использовать аннотации для решения этой проблемы.

В принципе, у вас есть @Nullable и @NotNull.

Вы можете использовать в методе и параметрах, например, так:

@NotNull public static String helloWorld() {
return "Hello World";
}

или

@Nullable public static String helloWorld() {
return "Hello World";
}

Второй пример не будет компилироваться (в IntelliJ IDEA).

При использовании первой helloWorld() функции в другом фрагменте кода:

public static void main(String[] args)
{
String result = helloWorld();
if(result != null) {
System.out.println(result);
}
}

Теперь компилятор IntelliJ IDEA сообщит вам, что проверка бесполезна, поскольку helloWorld() функция не вернет null, никогда.

Использование параметра

void someMethod(@NotNull someParameter) { }

если вы напишете что-то вроде:

someMethod(null);

Это не будет компилироваться.

Последний пример использования @Nullable

@Nullable iWantToDestroyEverything() { return null; }

Выполнение этого

iWantToDestroyEverything().something();

И вы можете быть уверены, что этого не произойдет. :)

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

В IntelliJ IDEA 10.5 и далее они добавили поддержку любых других @Nullable @NotNull реализаций.

Смотрите сообщение в блоге Более гибкие и настраиваемые аннотации @Nullable / @NotNull.

Ответ 3

Если значения null недопустимы

Если ваш метод вызывается извне, начните с чего-то вроде этого:

public void method(Object object) {
if (object == null) {
throw new IllegalArgumentException("...");
}

Затем, в остальной части этого метода, вы будете знать, что object не равно null.

Если это внутренний метод (не часть API), просто задокументируйте, что он не может быть null , и все.

Пример:

public String getFirst3Chars(String text) {
return text.subString(0, 3);
}

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

Если разрешено значение null

Это действительно зависит. Если обнаружите, что я часто делаю что-то подобное:

if (object == null) {
// something
} else {
// something else
}

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


На самом деле я редко использую идиому "if (object != null && ...".

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

Ответ 4

Вау, мне почти не хочется добавлять еще один ответ, когда у нас есть 57 различных способов рекомендовать NullObject pattern, но я думаю, что некоторым людям, интересующимся этим вопросом, может быть интересно узнать, что в таблице для Java 7 есть предложение добавить "null-безопасную обработку" - упрощенный синтаксис для логики if-not-equal-null .

Пример, приведенный Алексом Миллером, выглядит следующим образом:

public String getPostcode(Person person) {  
return person?.getAddress()?.getPostcode();
}

?. Означает отменять ссылку на левый идентификатор, только если он не равен null , в противном случае вычислите оставшуюся часть выражения как null. Некоторым людям, таким как член Java Posse Дик Уолл и избирателям Devoxx, действительно нравится это предложение, но есть и возражения на том основании, что это фактически будет способствовать большему использованию null в качестве контрольного значения.


Обновление: Официальное предложение по оператору, защищенному от нуля, в Java 7 было отправлено в рамках Project Coin. Синтаксис немного отличается от приведенного выше примера, но суть та же.


Обновление: Предложение оператора, защищенного от null, не попало в Project Coin. Таким образом, вы не увидите этот синтаксис в Java 7.

java