Избегать! = Нулевые заявления - PullRequest
3805 голосов
/ 07 ноября 2008

Я использую object != null много, чтобы избежать NullPointerException.

Есть ли хорошая альтернатива этому?

Например:

if (someobject != null) {
    someobject.doCalc();
}

Это позволяет избежать NullPointerException, когда неизвестно, является ли объект null или нет.

Обратите внимание, что принятый ответ может быть устаревшим, см. https://stackoverflow.com/a/2386013/12943 для более позднего подхода.

Ответы [ 62 ]

29 голосов
/ 24 марта 2012

Guava, очень полезная базовая библиотека от Google, имеет приятный и полезный API, чтобы избежать нулевых значений. Я считаю Использование AndAvoidingNullExplained очень полезным.

Как объяснено в вики:

Optional<T> - это способ замены нулевой ссылки T на ненулевое значение. Необязательный может содержать ненулевую ссылку T (в этом случае мы говорим, что ссылка «присутствует»), или она может содержать ничего (в этом случае мы говорим, что ссылка "отсутствует"). Это никогда сказано "содержать ноль".

Использование:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
22 голосов
/ 27 апреля 2015

Это очень распространенная проблема для каждого Java-разработчика. Так что в Java 8 есть официальная поддержка для решения этих проблем без загроможденного кода.

Java 8 представила java.util.Optional<T>. Это контейнер, который может содержать или не содержать ненулевое значение. Java 8 предоставила более безопасный способ обработки объекта, значение которого может быть нулевым в некоторых случаях. Он вдохновлен идеями Haskell и Scala .

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

В приведенном выше примере у нас есть фабрика обслуживания на дому, которая возвращает ручку нескольким бытовым приборам. Но эти услуги могут быть или не быть доступными / функциональными; это означает, что это может привести к NullPointerException. Вместо добавления нулевого условия if перед использованием какого-либо сервиса, давайте обернем его в Необязательный .

ОБОРОТ ДЛЯ ВАРИАНТА

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

public Optional<Service> getRefrigertorControl() {
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   }

Как видите, Optional.ofNullable() предоставляет простой способ обернуть ссылку. Есть и другие способы получить ссылку на Optional, либо Optional.empty() & Optional.of(). Один для возврата пустого объекта вместо перенастройки null, а другой для переноса ненулевого объекта соответственно.

ТАК КАК ТОЛЬКО ЭТО ПОМОГАЕТ ИЗБЕЖАТЬ ПУСТОЙ ПРОВЕРКИ?

После того, как вы обернули ссылочный объект, в Optional есть много полезных методов для вызова методов в обернутой ссылке без NPE.

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent вызывает данного Потребителя со ссылкой, если это ненулевое значение. В противном случае это ничего не делает.

@FunctionalInterface
public interface Consumer<T>

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

Мы очень часто используем троичный оператор для проверки нулевого условия и возвращаем альтернативное значение или значение по умолчанию. Необязательно предоставляет другой способ обработки того же условия без проверки нулевого значения. Optional.orElse (defaultObj) возвращает defaultObj, если Optional имеет нулевое значение. Давайте использовать это в нашем примере кода:

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

Теперь HomeServices.get () делает то же самое, но лучше. Проверяет, инициализирован ли сервис или нет. Если это так, верните то же самое или создайте новый новый сервис. Необязательный .orElse (T) помогает вернуть значение по умолчанию.

Наконец, вот наш NPE, а также нулевой код без проверки:

import java.util.Optional;
public class HomeServices {
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() {
    service = Optional.of(service.orElse(new HomeServices()));
    return service;
}

public Optional<Service> getRefrigertorControl() {
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);
}

public static void main(String[] args) {
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) {
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    }
}

public static void switchItOn(Service s){
         //...
    }
}

Полный текст сообщения: NPE, а также пустой код без проверки ... Действительно? .

21 голосов
/ 21 августа 2011

Мне нравятся статьи от Nat Pryce. Вот ссылки:

В статьях также есть ссылка на Git-репозиторий для Java Maybe Type, который мне кажется интересным, но я не думаю, что он сам по себе может уменьшить проверка кода наворотов. Проведя некоторые исследования в Интернете, я думаю, что ! = Null увеличение кода может быть уменьшено, в основном, благодаря тщательному проектированию.

19 голосов
/ 14 ноября 2008

Я попробовал NullObjectPattern, но для меня это не всегда лучший путь. Бывают случаи, когда «никаких действий» не уместно.

NullPointerException является Исключением времени выполнения , что означает, что это ошибка разработчиков, и при достаточном опыте она точно скажет, где находится ошибка.

Теперь к ответу:

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

Конечно, опыт - лучший способ понять и применить это предложение.

Byte!

16 голосов
/ 23 марта 2016

Вероятно, лучшая альтернатива для Java 8 или новее - использовать класс Optional.

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

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

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

Пример того, как вызвать исключение на нуль:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 представила метод Objects.requireNonNull, который может быть полезен, когда что-то должно проверяться на ненулевое значение. Пример:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();
15 голосов
/ 12 апреля 2014

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

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

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

Кроме того, я бы не рекомендовал использовать этот шаблон, когда тип предназначен для представления примитивного типа - как математические объекты, которые не являются скалярами: векторы, матрицы, комплексные числа и объекты POD (Plain Old Data), которые подразумеваются держать состояние в форме встроенных типов Java. В последнем случае вы должны вызывать методы-получатели с произвольными результатами. Например, что должен возвращать метод NullPerson.getName ()?

Стоит рассмотреть такие случаи, чтобы избежать абсурдных результатов.

15 голосов
/ 12 февраля 2014

Могу ли я ответить на это более широко!

Мы обычно сталкиваемся с этой проблемой, когда методы получают параметры не так, как мы ожидали (неправильный вызов метода - ошибка программиста). Например: вы ожидаете получить объект, вместо этого вы получите ноль. Вы ожидаете получить строку как минимум с одним символом, вместо этого вы получите пустую строку ...

Так что нет никакой разницы между:

if(object == null){
   //you called my method badly!

}

или

if(str.length() == 0){
   //you called my method badly again!
}

Они оба хотят убедиться, что мы получили действительные параметры, прежде чем выполнять какие-либо другие функции.

Как уже упоминалось в некоторых других ответах, во избежание вышеуказанных проблем вы можете следовать схеме Design by contract . Пожалуйста, смотрите http://en.wikipedia.org/wiki/Design_by_contract.

Чтобы реализовать этот шаблон в Java, вы можете использовать основные аннотации Java, такие как javax.annotation.NotNull или использовать более сложные библиотеки, такие как Hibernate Validator .

Просто образец:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

Теперь вы можете безопасно разрабатывать основную функцию вашего метода, не проверяя входные параметры, они защищают ваши методы от непредвиденных параметров.

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

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...
}
14 голосов
/ 14 июня 2013
  1. Никогда не инициализируйте переменные нулем.
  2. Если (1) невозможно, инициализируйте все коллекции и массивы пустыми коллекциями / массивами.

Делая это в своем собственном коде, вы можете избежать! = Пустых проверок.

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

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() {
    if (lemmings != null) for(lemming: lemmings) {
        // do something
    }
}



// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};

void checkLemmings() {
    for(lemming: lemmings) {
        // do something
    }
}

В этом есть небольшие накладные расходы, но оно того стоит для более чистого кода и меньшего количества исключений NullPointerExceptions.

14 голосов
/ 12 апреля 2014

Это самая распространенная ошибка, произошедшая для большинства разработчиков.

У нас есть несколько способов справиться с этим.

Подход 1:

org.apache.commons.lang.Validate //using apache framework

notNull (Объектный объект, Строковое сообщение)

Подход 2:

if(someObject!=null){ // simply checking against null
}

Подход 3:

@isNull @Nullable  // using annotation based validation

Подход 4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e){  
   if(e == null){
      throw new NullPointerException();
   }
   return e;
}
11 голосов
/ 13 октября 2010
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...