it-roy-ru.com

Избегать! = Нулевые заявления

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

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

Например:

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

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

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

3729
Goran Martinic

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

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

  1. Где ноль является действительным ответом с точки зрения договора; а также

  2. Где это не правильный ответ.

(2) легко. Либо используйте операторы assert (утверждения), либо допустите ошибку (например, NullPointerException ). Утверждения - это сильно недоиспользуемая функция Java, которая была добавлена ​​в 1.4. Синтаксис:

assert <condition>

или же

assert <condition> : <object>

где <condition> - логическое выражение, а <object> - объект, чей вывод метода toString() будет включен в ошибку.

Оператор assert выбрасывает Error (AssertionError), если условие не выполняется. По умолчанию Java игнорирует утверждения. Вы можете включить утверждения, передав опцию -ea в JVM. Вы можете включать и отключать утверждения для отдельных классов и пакетов. Это означает, что вы можете проверять код с помощью утверждений при разработке и тестировании, а также отключать их в производственной среде, хотя мое тестирование показало, что утверждения не влияют практически на производительность.

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

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

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

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

public interface Action {
  void doSomething();
}

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

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

Альтернативное решение состоит в том, чтобы никогда не возвращать нуль и вместо этого использовать шаблон 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);
        }
    }
}
2487
cletus

Если вы используете (или планируете использовать) 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 аннотации.

559
Luca Molteni

Если нулевые значения не разрешены

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

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

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

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

Пример:

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

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

Если ноль разрешен

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

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

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


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

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

296
myplacedk

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

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

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

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


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


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

222
erickson

Если неопределенные значения не разрешены:

Вы можете настроить свой IDE так, чтобы он предупреждал вас о возможной нулевой разыменованию. Например. в Eclipse см. Предпочтения> Java> Компилятор> Ошибки/Предупреждения/Нулевой анализ.

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

Если вы хотите определить новый API, в котором неопределенные значения имеют смысл, используйте Option Pattern (может быть знакомо по функциональным языкам). Имеет следующие преимущества:

  • В API явно указывается, существует ли вход или выход или нет.
  • Компилятор заставляет вас обрабатывать «неопределенный» случай.
  • Опция является монадой , поэтому нет необходимости в подробной проверке нуля, просто используйте map/foreach/getOrElse или аналогичный комбинатор для безопасного использования значения (пример) .

Java 8 имеет встроенный Optional класс (рекомендуется); для более ранних версий есть альтернативные библиотеки, например Guava 's Optional или FunctionalJava ' s Option . Но, как и во многих шаблонах в функциональном стиле, использование Option в Java (даже 8) приводит к некоторому шаблону, который можно сократить, используя менее подробный язык JVM, например, Скала или Xtend.

Если вам приходится иметь дело с API, который может возвращать нули, вы не сможете многое сделать в Java. Xtend и Groovy имеют оператор Elvis?: и нулевой безопасный оператор разыменования?., но имейте в виду, что в случае нулевой ссылки это возвращает null, поэтому он просто «откладывает» правильную обработку нулевого значения.

184
thSoft

Только для этой ситуации -

Не проверять, является ли переменная нулевой, прежде чем вызывать метод equals (пример сравнения строк ниже):

if ( foo.equals("bar") ) {
 // ...
}

приведет к NullPointerException, если foo не существует.

Вы можете избежать этого, сравнивая свои Strings следующим образом:

if ( "bar".equals(foo) ) {
 // ...
}
169
echox

С Java 8 поставляется новый класс Java.util.Optional, который, возможно, решает некоторые проблемы. Можно, по крайней мере, сказать, что это улучшает читабельность кода, а в случае открытых API-интерфейсов делает контракт API-интерфейса более понятным для разработчика клиента.

Они работают так:

Необязательный объект для данного типа (Fruit) создается как тип возврата метода. Он может быть пустым или содержать объект Fruit:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {
   for (Fruit fruit : fruits) {
      if (fruit.getName().equals(name)) {
         return Optional.of(fruit);
      }
   }
   return Optional.empty();
}

Теперь посмотрите на этот код, где мы ищем список Fruit (fruits) для данного экземпляра Fruit:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
   Fruit fruit = found.get();
   String name = fruit.getName();
}

Вы можете использовать оператор map() для вычисления или извлечения значения из необязательного объекта. orElse() позволяет вам предоставить запасной вариант для пропущенных значений.

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

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

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

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

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

Я предлагаю вам прочитать эту статью (мой основной источник для написания этого ответа), в которой хорошо объяснены проблемная проблема NullPointerException (и вообще нулевой указатель), а также (частичное) решение, принесенное Optional: Необязательные объекты Java.

148
Pierre Henry

В зависимости от того, какие объекты вы проверяете, вы можете использовать некоторые классы в общих достояниях Apache, такие как: Apache commons lang и Коллекции общих Apache

Пример: 

String foo;
...
if( StringUtils.isBlank( foo ) ) {
   ///do something
}

или (в зависимости от того, что вам нужно проверить):

String foo;
...
if( StringUtils.isEmpty( foo ) ) {
   ///do something
}

Класс StringUtils - только один из многих; Есть много хороших классов в общем достоянии, которые делают нулевую безопасную манипуляцию.

Ниже приведен пример того, как вы можете использовать null vallidation в Java при включении библиотеки Apache (commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);
}

И если вы используете Spring, Spring также имеет те же функции в своем пакете, см. Библиотеку (spring-2.4.6.jar) 

Пример того, как использовать этот статический класс из Spring (org.springframework.util.Assert)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");
119
javamonkey79
  • Если вы считаете, что объект не должен быть нулевым (или это ошибка), используйте assert.
  • Если ваш метод не принимает нулевые параметры, скажите это в javadoc и используйте assert.

Вы должны проверять объект! = Null, только если вы хотите обработать случай, когда объект может быть нулевым ...

Есть предложение добавить новые аннотации в Java7, чтобы помочь с нулевыми/notnull параметрами: http://tech.puredanger.com/Java7/#jsr308

91
pgras

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

85
Alex Worden

Каркас коллекций Google предлагает хороший и элегантный способ выполнить нулевую проверку.

В классе библиотеки есть такой метод:

static <T> T checkNotNull(T e) {
   if (e == null) {
      throw new NullPointerException();
   }
   return e;
}

И использование это (с import static):

...
void foo(int a, Person p) {
   if (checkNotNull(p).getAge() > a) {
      ...
   }
   else {
      ...
   }
}
...

Или в вашем примере:

checkNotNull(someobject).doCalc();
72
user2427

Иногда у вас есть методы, которые работают с его параметрами, которые определяют симметричную операцию:

a.f(b); <-> b.f(a);

Если вы знаете, что b никогда не может быть нулевым, вы можете просто поменять его местами. Это наиболее полезно для equals: Вместо foo.equals("bar"); лучше сделать "bar".equals(foo);.

71
Johannes Schaub - litb

Вместо шаблона нулевого объекта - который имеет свои применения - вы можете рассмотреть ситуации, когда нулевой объект является ошибкой.

Когда выдается исключение, изучите трассировку стека и проработайте ошибку.

69
Jim Nelson

В Java 7 появился новый служебный класс Java.util.Objects, в котором есть метод requireNonNull(). Все, что это делает, это генерирует NullPointerException, если его аргумент равен нулю, но он немного очищает код. Пример:

Objects.requireNonNull(someObject);
someObject.doCalc();

Этот метод наиболее полезен для проверки непосредственно перед присваиванием в конструкторе, где каждое его использование может сохранить три строки кода:

Parent(Child child) {
   if (child == null) {
      throw new NullPointerException("child");
   }
   this.child = child;
}

становится

Parent(Child child) {
   this.child = Objects.requireNonNull(child, "child");
}
66
Stuart Marks

Нуль не является «проблемой». Это неотъемлемая часть полного набора инструментов для моделирования. Программное обеспечение стремится моделировать сложность мира и ноль несет свое бремя. Null означает «Нет данных» или «Неизвестно» в Java и т.п. Поэтому для этих целей целесообразно использовать нули. Я не предпочитаю шаблон «Нулевой объект»; Я думаю, что это поднимает проблему ' кто будет охранять Стражи '. 
Если вы спросите меня, как зовут мою подругу, я скажу вам, что у меня нет подруги. На языке Java я верну ноль. Альтернативой будет выбрасывать значимое исключение, чтобы указать на некоторую проблему, которая не может быть (или не хочет быть) решена прямо здесь, и делегировать ее где-то выше в стеке, чтобы повторить или сообщить пользователю об ошибке доступа к данным , 

  1. Для «неизвестного вопроса» дайте «неизвестный ответ». (Будьте нуль-безопасны, если это правильно с деловой точки зрения) Проверка аргументов для нуля один раз внутри метода перед использованием освобождает нескольких вызывающих от проверки их перед вызовом.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    }
    

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

    getPhotoOfThePerson(me.getGirlfriend())
    

    И это подходит с новым Java API (с нетерпением жду)

    getPhotoByName(me.getGirlfriend()?.getName())
    

    Хотя для некоторых людей это довольно «обычный бизнес-процесс» - не найти фотографию, хранящуюся в БД, но в некоторых других случаях я использовал пары, подобные приведенным ниже.

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);
    

    И не надо набирать <alt> + <shift> + <j> (генерировать javadoc в Eclipse) и написать три дополнительных слова для вашего публичного API. Этого будет более чем достаточно для всех, кроме тех, кто не читает документацию.

    /**
     * @return photo or null
     */
    

    или же

    /**
     * @return photo, never null
     */
    
  2. Это довольно теоретический случай, и в большинстве случаев вы должны предпочесть Java нуль-безопасный API (в случае, если он будет выпущен через 10 лет), но NullPointerException является подклассом Exception. Таким образом, это форма Throwable, которая указывает условия, которые может потребоваться разумному приложению ( javadoc )! Чтобы использовать первое наиболее значительное преимущество исключений и отделить код обработки ошибок от «обычного» кода ( по словам создателей Java ), как мне кажется, целесообразно поймать NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;
        }
    }
    

    Могут возникнуть вопросы:

    В. Что если getPhotoDataSource() возвращает ноль?
    A. Это зависит от бизнес-логики. Если я не смогу найти фотоальбом, я не покажу вам фотографий. Что если appContext не инициализирован? Бизнес-логика этого метода мирится с этим. Если та же логика должна быть более строгой, чем генерирование исключения, это является частью бизнес-логики, и следует использовать явную проверку на нулевое значение (случай 3). New Java Null-safe API лучше подходит для того, чтобы выборочно указывать, что подразумевается, а что не означает, что инициализация должна быть быстрой в случае ошибок программиста.

    Q. Избыточный код может быть выполнен и ненужные ресурсы могут быть захвачены.
    A. Это может произойти, если getPhotoByName() попытается открыть соединение с базой данных, создать PreparedStatement и, наконец, использовать имя человека в качестве параметра SQL. Подход для неизвестного вопроса дает неизвестный ответ (случай 1) работает здесь. Перед захватом ресурсов метод должен проверить параметры и вернуть «неизвестный» результат, если это необходимо.

    Q. Этот подход имеет снижение производительности из-за открытия попытки закрытия.
    A. Программное обеспечение должно быть легко понять и изменить в первую очередь. Только после этого можно думать о производительности и только при необходимости! и где нужно! ( источник ) и многие другие).

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

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
        }
    }
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
                        }
                    }
                }
            }
        }
        return result;
    }
    

    PPS. Для тех, кто быстро понижает голос (и не так быстро читает документацию), я хотел бы сказать, что я никогда не ловил исключение с нулевым указателем (NPE) в своей жизни. Но эта возможность была намеренно разработана создателями Java, потому что NPE является подклассом Exception. У нас есть прецедент в истории Java, когда ThreadDeath является Error не потому, что на самом деле это ошибка приложения, а исключительно потому, что она не была предназначена для перехвата! Сколько NPE подходит, чтобы быть Error, чем ThreadDeath! Но это не так.

  3. Проверять наличие «Нет данных», только если это подразумевает бизнес-логика.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
        } else {
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        }
    }
    

    а также

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    }
    

    Если appContext или dataSource не инициализирован, необработанная среда выполнения NullPointerException уничтожит текущий поток и будет обработана Thread.defaultUncaughtExceptionHandler (для вас можно определить и использовать ваш любимый регистратор или другой механизм уведомлений). Если не установлено, ThreadGroup # uncaughtException выведет трассировку стека в системную ошибку. Необходимо следить за журналом ошибок приложения и открывать проблему Jira для каждого необработанного исключения, которое фактически является ошибкой приложения. Программист должен исправить ошибку где-то в инициализации.

65
Mykhaylo Adamovych

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

  • В Objective-C вы можете сделать эквивалентный вызов метода для nil, и абсолютно ничего не произойдет. Это делает ненужными большинство нулевых проверок, но может значительно усложнить диагностику ошибок.
  • В Nice , производном от Java языке, есть две версии всех типов: потенциально нулевая версия и ненулевая версия. Вы можете вызывать методы только для ненулевых типов. Потенциально нулевые типы могут быть преобразованы в ненулевые типы с помощью явной проверки на нулевые. Это позволяет намного легче узнать, где необходимы нулевые проверки, а где нет.
48
Michael Borgwardt

Действительно распространенная «проблема» в Java.

Сначала мои мысли по этому поводу:

Я считаю, что плохо «что-то есть», когда передается NULL, где NULL не является допустимым значением. Если вы не выходите из метода с какой-то ошибкой, это означает, что в вашем методе ничего не пошло не так, что неверно. Тогда вы, вероятно, вернете нуль в этом случае, а в методе получения вы снова проверяете на ноль, и он никогда не заканчивается, и в итоге вы получаете «if! = Null» и т.д.

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

Я решил эту проблему так:

Во-первых, я следую этому соглашению:

  1. Все публичные методы/API всегда проверяют свои аргументы на ноль
  2. Все приватные методы не проверяют наличие null, поскольку они являются контролируемыми методами (просто дайте умереть с исключением nullpointer, если это не было обработано выше)
  3. Единственные другие методы, которые не проверяют на ноль, - это служебные методы. Они общедоступны, но если вы по каким-то причинам вызываете их, вы знаете, какие параметры вы передаете. Это все равно что пытаться кипятить воду в чайнике без воды ...

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

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

Обратите внимание, что addParam () возвращает self, так что вы можете добавить больше параметров для проверки.

Метод validate() сгенерирует проверенный ValidationException, если какой-либо из параметров имеет значение null (проверенный или непроверенный является скорее проблемой дизайна/вкуса, но мой ValidationException проверен).

void validate() throws ValidationException;

Сообщение будет содержать следующий текст, если, например, «plan» имеет значение null:

" Недопустимое значение аргумента null для параметра [планы] "

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

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

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

34
Oleg

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

  1. позвольте исключениям проходить сквозь - поймайте их в «главном цикле» или в какой-либо другой управляющей подпрограмме.

    • проверять наличие ошибок и обрабатывать их соответствующим образом

Конечно, взгляните также на Аспектно-ориентированное программирование - у них есть изящные способы вставить if( o == null ) handleNull() в ваш байт-код.

32
xtofl

В дополнение к использованию assert вы можете использовать следующее:

if (someobject == null) {
    // Handle null here then move on.
}

Это немного лучше чем:

if (someobject != null) {
    .....
    .....



    .....
}
32
fastcodejava

Просто никогда не используйте нуль. Не позволяй это.

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

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

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

class C {
    private final MyType mustBeSet;
    public C(MyType mything) {
       mustBeSet=Contract.notNull(mything);
    }
   private String name = "<unknown>";
   public void setName(String s) {
      name = Contract.notNull(s);
   }
}


class Contract {
    public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}

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

30
ianpojman

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

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

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

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

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
28
Murat Derya Özen

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

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

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

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

ОБРАЩЕНИЕ К ВАРИАНТУ <T>

Давайте рассмотрим метод, чтобы получить ссылку на сервис с завода. Вместо того, чтобы возвращать сервисную ссылку, оберните ее необязательно. Это позволяет пользователю 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>

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

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

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

Теперь HomeServices.get () делает то же самое, но лучше. Проверяет, инициализирован ли сервис или нет. Если это так, верните то же самое или создайте новый новый сервис. Необязательный <T> .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
Yogesh Devatraj

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

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

20
Mr Palo

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

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

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

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

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

Байт!

18
OscarRyz

Вероятно, лучшая альтернатива для 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
Raghu K Nair

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

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

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

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

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

14
spectre

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

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

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

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
Alireza Fattahi
  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
    }
}

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

13
Stuart Axon

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

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

Подход 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;
}
13
Sireesh Yarlagadda
public static <T> T ifNull(T toCheck, T ifNull) {
    if (toCheck == null) {
           return ifNull;
    }
    return toCheck;
}
10
tltester

Java 8 представила новый класс Необязательный в пакете Java.util.

Преимущества Java 8 Необязательно:

1.) Нулевые проверки не требуются.
2.) Нет больше NullPointerException во время выполнения.
3.) Мы можем разработать чистые и аккуратные API. 

Необязательно - Контейнерный объект, который может содержать или не содержать ненулевое значение. Если значение присутствует, isPresent () вернет true, а get () вернет значение.

Для получения более подробной информации, обратитесь к документации Oracle: - https://docs.Oracle.com/javase/8/docs/api/Java/util/Optional.html

9
NeeruSingh

Вы можете использовать FindBugs . У них также есть Eclipse плагин), который помогает вам найти дубликаты нулевых проверок (среди прочего), но имейте в виду, что иногда вы должны выбрать защитное программирование. Есть также Контракты для Java , которые могут быть полезны.

8
Leen Toelen

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

  1. Избегайтеленивой инициализациипеременных-членов в максимально возможной степени. Инициализируйте переменные в самом объявлении. Это будет обрабатывать исключения NullPointerException. 

  2. Определитеmutabilityпеременных-членов в начале цикла. Эффективно используйте языковые конструкции, такие как ключевое слово final

  3. Если вы знаете, что дополнения для метода не будут изменены, объявите их как final.

  4. Максимально ограничьтеmutationданных. Некоторые переменные могут быть созданы в конструкторе и никогда не могут быть изменены.Удалить общедоступные методы установки, если они действительно не требуются.

    Например. Предположим, что один класс в вашем приложении (A.Java) поддерживает коллекцию, подобную HashMap. Не предоставляйте метод получения public в A.Java и разрешайте B.Java напрямую добавлять элемент в Map. Вместо этого предоставьте API в A.Java, который добавляет элемент в коллекцию. 

    // Avoid
    a.getMap().put(key,value)
    
    //recommended
    
    public void addElement(Object key, Object value){
           // Have null checks for both key and value here : single place
           map.put(key,value);
    }
    
  5. И наконец, эффективно используйте блоки try{} catch{} finally{} в нужных местах.

7
Ravindra babu

Поскольку Java 7 класс Java.util.Objects существует.

Но поскольку Java 8, вы можете использовать методы Objects.isNull(var) и Objects.nonNull(var) класса Objects для проверки нулевого указателя.

Например,

String var1 = null;
Date var2 = null;
Long var3 = null;

if(Objects.isNull(var1) && Objects.isNull(var2) && Objects.isNull(var3))
    System.out.println("All Null");
else if (Objects.nonNull(var1) && Objects.nonNull(var2) && Objects.nonNull(var3))
    System.out.println("All Not Null");
7
Philip John

В общем, чтобы избежать заявления

if (object != null) {
    ....
}
  1. начиная с Java 7 вы можете использовать методы Objects:

    Objects.isNull (объект)

    Objects.nonNull (объект)

    Objects.requireNonNull (объект)

    Objects.equals (объект1, объект2)

  2. начиная с Java 8 вы можете использовать необязательный класс ( когда использовать

object.ifPresent(obj -> ...); Java 8

object.ifPresentOrElse(obj -> ..., () -> ...); Java 9

  1. полагаться на контракт метода ( JSR 305 ) и использовать Найти ошибки . Пометьте свой код аннотациями @javax.annotation.Nullable и @javax.annotation.Nonnnul. Также предварительные условия доступны.

    Preconditions.checkNotNull (объект);

  2. В особых случаях (например, для строк и коллекций) вы можете использовать служебные методы Apache-commons (или Google guava):

public static boolean isEmpty (CharSequence cs) // Apache CollectionUtils

public static boolean isEmpty (Collection coll) // Apache StringUtils

public static boolean isEmpty (Карта map) // Apache MapUtils

public static boolean isNullOrEmpty (@Nullable String string) // Строки Гуавы

  1. Когда вам нужно назначить значение по умолчанию, когда null, используйте Apache commons lang

публичный статический объект defaultIfNull (объект объекта, объект defaultValue)

6
fbokovikov

Еще одна альтернатива:

Следующая простая функция помогает скрыть нулевую проверку (я не знаю почему, но я не нашел ее как часть той же common library):

public static <T> boolean isNull(T argument) {
    return (argument == null);
}

Теперь вы можете написать

if (!isNull(someobject)) {
    someobject.doCalc();
}

иМО - лучший способ выразить != null.

6
jeha

Куда бы вы ни передавали массив или вектор, инициализируйте их пустыми вместо нуля. - Таким образом, вы можете избежать много проверок на ноль, и все хорошо :)

public class NonNullThing {

   Vector vectorField = new Vector();

   int[] arrayField = new int[0];

   public NonNullThing() {

      // etc

   }

}
5
Stuart Axon

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

  1. public static boolean isNull(Object obj)

    Возвращает true, если предоставленная ссылка равна нулю, в противном случае возвращает False.

    Начиная с Java 1.8

  2. public static boolean nonNull(Object obj)

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

    Начиная с Java 1.8

  3. public static <T> T requireNonNullElse​(T obj, T defaultObj)

    Возвращает первый аргумент, если он не равен нулю, а в противном случае возвращает Ненулевой второй аргумент.

    С Java 9

  4. public static <T> T requireNonNullElseGet​(T obj, Supplier<? extends T> supplier)

    Возвращает первый аргумент, если он не равен NULL, а в противном случае возвращает ненулевое значение supplier.get ().

    С Java 9

  5. public static <T> T requireNonNull​(T obj, Supplier<String> messageSupplier)

    Проверяет, что указанная ссылка на объект не является нулем, и в противном случае создает настраиваемое исключение NullPointerException.

    Начиная с Java 1.8

Более подробную информацию о вышеупомянутых функциях можно найти здесь .

5
Mukesh A

Я считаю, что предварительные условия для гуавы очень полезны в этом случае. Мне не нравится оставлять нулевые значения исключением нулевого указателя, поскольку единственный способ понять NPE - найти номер строки. Номера строк в рабочей версии и в разрабатываемой версии могут быть разными.

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

Например,

Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");
5
Gal Morad

Вы можете использовать перехватчик перед вызовом метода. Вот на что аспектно-ориентированное программирование сосредоточиться.

Предположим, M1 (Object test) - это метод, а M2 - метод, в котором мы применяем аспект перед вызовом метода, M2(Object test2). Если test2 != null, тогда вызовите M1, иначе сделайте другое. Он работает для всех методов, для которых вы хотите применить аспект. Если вы хотите применить аспект для поля экземпляра и конструктора, вы можете использовать AspectJ . Spring также может быть лучшим выбором для аспекта метода.

4
abishkar bhattarai

В Java 8 вы можете использовать тип T для локальной переменной/поля/метода-аргумента/метода-возвращаемого типа, если он никогда не назначал null (и не проверять null), или ввести Optional<T>, если он может быть null. Затем используйте метод map для обработки T -> и метод flatMap для обработки T -> Optional<R>:

class SomeService {
    @Inject
    private CompanyDao companyDao;

    // return Optional<String>
    public Optional<String> selectCeoCityByCompanyId0(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity);
    }

    // return String + default value
    public String selectCeoCityByCompanyId1(int companyId) {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElse("UNKNOWN");
    }

    // return String + exception
    public String selectCeoCityByCompanyId2(int companyId) throws NoSuchElementException {
        return companyDao.selectById(companyId)
                .map(Company::getCeo)
                .flatMap(Person::getHomeAddress)
                .flatMap(Address::getCity)
                .orElseThrow(NoSuchElementException::new);
    }
}

interface CompanyDao {
    // real situation: no company for such id -> use Optional<Company> 
    Optional<Company> selectById(int id);
}

class Company {
    // company always has ceo -> use Person 
    Person ceo;
    public Person getCeo() {return ceo;}
}

class Person {
    // person always has name -> use String
    String firstName;
    // person can be without address -> use Optional<Address>
    Optional<Address> homeAddress = Optional.empty();

    public String getFirstName() {return firstName;}   
    public Optional<Address> getHomeAddress() {return homeAddress;}
}

class Address {
    //  address always contains country -> use String
    String country;
    //  city field is optional -> use Optional<String>
    Optional<String> city = Optional.empty();

    String getCountry() {return country;}    
    Optional<String> getCity() {return city;}
}
4
Ivan Golovach

Если вы используете Java8 или новее, перейдите к isNull(yourObject) из Java.util.Objects.

Пример:-

String myObject = null;

Objects.isNull(myObject); //will return true
3
Jobin Joseph

Java 8 теперь имеет необязательный класс, который оборачивает рассматриваемый объект, и, если присутствует значение, isPresent () вернет true, а get () вернет значение.

http://www.Oracle.com/technetwork/articles/Java/java8-optional-2175753.html

3
sidgate

Вы также можете использовать Checker Framework (с JDK 7 и выше) для статической проверки на нулевые значения. Это может решить множество проблем, но требует запуска дополнительного инструмента, который в настоящее время работает только с OpenJDK AFAIK. https://checkerframework.org/

3
Jochen

Ладно, теперь я технически отвечал на это миллион раз, но я должен сказать это, потому что это бесконечная дискуссия с Java-программистами.

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

Я говорю это потому, что у меня большой опыт программирования на C++, а мы этого не делаем. Другими словами, вам не нужно. И обратите внимание, что в Java, если вы нажмете на висячий указатель, вы получите нормальное исключение; в C++ это исключение обычно не перехватывается и завершает программу.

Не хотите сделать это? Затем следуйте простым правилам C/C++.

Не создавайте вещи так легко, думайте, что каждое «новое» может принести вам много неприятностей и СЛЕДОВАТЬ этим простым правилам.

Класс должен получить доступ к памяти только тремя способами ->

  1. Он может «Иметь» членов класса, и они будут следовать этим правилам:

    1. ВСЕ члены "HAS" создаются "новыми" в конструкторе.
    2. Вы закроете/выделите в деструкторе или эквивалентной функции close () В Java для того же класса и в другом NO.

Это означает, что вам нужно иметь в виду (как Java), кто является владельцем или родителем каждого ресурса, и уважать это право собственности. Объект удаляется только тем классом, который его создал. Также ->

  1. Некоторые участники будут «ИСПОЛЬЗОВАНЫ», но не будут принадлежать или «ИМЕТЬ». Это «СВОЙ» в другом классе и передается в качестве аргументов конструктору. Так как они принадлежат другому классу, мы НИКОГДА не будем удалять или закрывать это, только родитель может.

  2. Метод в классе также может создавать экземпляры локальных объектов для внутреннего использования, которые НИКОГДА не будут выходить за пределы класса, или они должны были быть обычными объектами «имеет».

Наконец, чтобы все это работало, вам нужно иметь дисциплинированный дизайн с классами в форме иерархии и без циклов.

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

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

2
Alex Vaz

Способ избежать ненужного null-checks прост:

You need to know which variables can be null, and which cannot, and you need to be confident about which category a given variable fall into.

Но, хотя это можно сформулировать достаточно просто, его достижение сложнее. Ключ лежит в части confident, потому что как вы можете быть уверены, что переменная не может быть нулевой?

На это нет быстрых, простых ответов, но вот несколько советов:

  1. Чистый код. Самая важная вещь, позволяющая рассуждать о поведении фрагмента кода, заключается в том, что он написан в простом для понимания предмете. Назовите ваши переменные в зависимости от того, что они представляют, назовите ваши методы после того, что они делают, примените Single responsibility principle (S в SOLID: http://en.wikipedia.org/wiki/SOLID_(object-oriented_design) , это означает, что каждый кусок кода должен нести одну ответственность, и делать это и ничего больше). После того как ваш код станет чистым, об этом будет гораздо легче рассуждать, в том числе на разных уровнях кода. В грязном коде попытка понять, что делает метод, может заставить вас забыть, почему вы читаете метод в первую очередь. (Совет: прочитайте «Чистый код» Роберта С. Мартина)

  2. Избегайте возврата значений null. Если значение null будет препятствовать правильной работе вашей программы, вместо этого выведите exception (обязательно добавьте соответствующую обработку ошибок.) В тех случаях, когда возвращение значения null может быть приемлемым, например, при попытке извлечь объект из базы данных. В этих случаях напишите код, который обрабатывает значения null, и запишите за ухом, что здесь у нас есть кое-что, что может вернуть null. Обработайте возвращенные значения null как можно ближе к вызывающему методу, возвращающему null, насколько это возможно (не просто слепо передавайте его обратно в цепочку вызовов.)

  3. Никогда НИКОГДА не передавайте явные значения null в качестве параметров (по крайней мере, между классами). Если вы когда-либо находитесь в положении, в котором передача параметра null- является единственной возможностью, создание нового метода, у которого нет этого параметра, - это путь. 

  4. Подтвердите ваш вклад! Определите «точки входа» в ваше приложение. Они могут все: веб-сервисы, REST-сервисы, удаленные EJB-классы, контроллеры и т.д. Для каждого метода в этих точках входа задайте себе вопрос: «Будет ли этот метод работать правильно, если этот параметр имеет значение null?» Если ответ отрицательный, добавьте Validate.notNull(someParam, "Can't function when someParam is null!");. Это выдаст IllegalArgumentException, если требуемый параметр отсутствует. Хорошая вещь в этом типе проверки в точках входа состоит в том, что вы можете легко предположить в коде, выполняемом из точки входа, что эта переменная никогда не будет нулевой! Кроме того, если это не удается, находясь на начальном этапе, отладка становится намного проще, чем если бы вы просто получили NullPointerException в глубине своего кода, поскольку подобный сбой может означать только одно: клиент не сделал этого. вышлю вам всю необходимую информацию. В большинстве случаев вы хотите проверить все входные параметры, если вы попадаете в положение, где вам нужно разрешить много null- значений, это может быть признаком плохо спроектированного интерфейса, который нуждается в рефакторинге/дополнениях для удовлетворения потребностей. клиентов.

  5. При работе с Collections верните пустой, а не ноль! 

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

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

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

Конечно, вам все равно придется проверять на ноль, но она может обрезаться до минимума (т. Е. Ситуация, когда знаю вы можете получить нулевое значение, а не везде, просто чтобы быть уверенным.) Когда это произойдет. для нулевых проверок я на самом деле предпочитаю использовать троичный оператор (но используйте осторожно, когда вы начинаете их вкладывать, они оказываются очень грязными).

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "null";
}
2
Tobb

Прежде всего, мы не можем действительно удалить все нулевые условия. Мы можем уменьшить их, используя аннотации @NotNull и @Nullable (как уже упоминалось). Но это должно быть подкреплено некоторыми рамками. Вот где OVal может помочь.

Основная идея заключается в том, что объект/параметры/конструктор всегда должны удовлетворять предварительным условиям. У вас может быть множество предварительных условий, таких как Nullable, NotNull и OVal позаботятся о том, чтобы при вызове объект находился в согласованном состоянии.

Я предполагаю, что OVal внутренне использует AspectJ для проверки предварительных условий.

@Guarded
public class BusinessObject
{
  public BusinessObject(@NotNull String name)
  {
    this.name = name;
  }

  ...
}

Например,

// Throws a ConstraintsViolatedException because parameter name is null
BusinessObject bo = new BusinessObject(null);
2
Vinay Lodha

Для этой проблемы мы использовали библиотеки Apache (Apache Commons).

ObjectUtils.equals(object, null)

или же

CollectionUtils.isEmpty(myCollection);

или же 

StringUtils.isEmpty("string");

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

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

Надеюсь это поможет. 

1
iowatiger08

Я предпочитаю это

public void simpleFunc(SomeObject someObject){
    someObject = someObject != null ? someObject : new SomeObject(null);
    someObject.doSomething();
}

Конечно, в моем примере SomeObject изящно обрабатывает нулевой параметр. Например, регистрация такого события и больше ничего не делать.

1
drzymala

В Java 8 вы можете передать поставщика вспомогательному методу, как показано ниже:

if(CommonUtil.resolve(()-> a.b().c()).isPresent()) {

}

Выше заменяет код котельной плиты, как показано ниже,

if(a!=null && a.b()!=null && a.b().c()!=null) {

}

//CommonUtil.Java

 public static <T> Optional<T> resolve(Supplier<T> resolver) {
        try {
            T result = resolver.get();
            return Optional.ofNullable(result);
        } catch (NullPointerException var2) {
            return Optional.empty();
        }
    }
1
nilesh

С лямбдами Java 8 можно определить утилитарные методы, которые обрабатывают вложенные нулевые проверки почти симпатичным способом.

void example() {
    Entry entry = new Entry();
    // This is the same as H-MANs solution 
    Person person = getNullsafe(entry, e -> e.getPerson());    
    // Get object in several steps
    String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());
    // Call void methods
    doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());        
}

/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */
public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {
    if (o1 != null) return f1.apply(o1);
    return null; 
}

public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {
    return getNullsafe(getNullsafe(o0, f1), f2);
}

public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {
    return getNullsafe(getNullsafe(o0, f1, f2), f3);
}


/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */
public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {
    if (o1 != null) f1.accept(o1);
}

public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {
    doNullsafe(getNullsafe(o0, f1), f2);
}

public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {
    doNullsafe(getNullsafe(o0, f1, f2), f3);
}


class Entry {
    Person getPerson() { return null; }
}

class Person {
    Name getName() { return null; }
}

class Name {
    void nameIt() {}
    String getGivenName() { return null; }
}

(Этот ответ был впервые опубликован здесь .)

1
Lii

Kotlin с нулевой безопасностью - элегантная альтернатива, но это означает большее изменение.

0
Francis

Для служебных классов вы можете проверить, что параметры не равны NULL.

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

0
daniel

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

Это одна из веских причин (из многих) использовать модульное тестирование.

0
Mehdi

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

public abstract class SomeObject {
   public abstract boolean isNil();
}

public class NullObject extends SomeObject {
   @Override
   public boolean isNil() {
      return true;
   }
}
public class RealObject extends SomeObject {
   @Override
   public boolean isNil() {
      return false;
   }
}

Теперь стоит проверить,

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

Мы можем использовать,

if (!someObject.isNil()) {
   someobject.doCalc();
}

Ссылка: https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm

0
Vidura Mudalige

Один вариант у вас есть

  • Используйте checker framework s @RequiresNonNull для методов. Например, вы получаете это, если вызываете метод, аннотированный как таковой, с нулевым аргументом. Это не удастся во время компиляции, даже до запуска вашего кода! так как во время выполнения это будет NullPointerException

    @RequiresNonNull(value = { "#1" })
    static void check( Boolean x) {
        if (x) System.out.println("true");
        else System.out.println("false");
    }
    
    public static void main(String[] args) {
    
    
        check(null);
    
    }
    

получает 

[ERROR] found   : null
[ERROR] required: @Initialized @NonNull Boolean
[ERROR] -> [Help 1]

Существуют и другие методы, такие как «Использовать необязательный» в Java 8, аннотации в Guava, шаблон пустых объектов и т.д. Не имеет значения, если вы добились своей цели избежать! = Null

0
Binod Pant

Еще одна альтернатива проверке! = Null (если вы не можете избавиться от нее по проекту):

Optional.ofNullable(someobject).ifPresent(someobject -> someobject.doCalc());

или же

Optional.ofNullable(someobject).ifPresent(SomeClass::doCalc);

С SomeClass, являющимся типом someobject.

Вы не можете получить возвращаемое значение из doCalc (), так что это полезно только для методов void.

0
Sebastian Brandt

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

    BiConsumer<Object, Consumer<Object>> consumeIfPresent  = (s,f) ->{
        if(s!=null) {
            f.accept(s);
        }
    };

    consumeIfPresent.accept(null, (s)-> System.out.println(s) );
    consumeIfPresent.accept("test", (s)-> System.out.println(s));

    BiFunction<Object, Function<Object,Object>,Object> executeIfPresent  = (a,b) ->{
        if(a!=null) {
            return b.apply(a);
        }
        return null;
    };
    executeIfPresent.apply(null, (s)-> {System.out.println(s);return s;} );
    executeIfPresent.apply("test", (s)-> {System.out.println(s);return s;} );
0
Ramprabhu