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

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

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

Например:

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

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

Обратите внимание, что принятый ответ может быть устаревшим, см. /questions/42604409/izbegat-nulevyie-zayavleniya/42604515#42604515 для более недавнего подхода.

71 ответ

Решение

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

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

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

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

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 && ...".

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

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

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

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

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


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


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

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

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

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

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

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

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

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

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

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

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

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

Вы можете избежать этого, если вы сравните Stringвот так:

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

С 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.

В зависимости от того, какие объекты вы проверяете, вы можете использовать некоторые классы в общих фондах Apache, такие как: Apache Commons Lang и Apache Commons Collection.

Пример:

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)

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

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

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

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

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

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

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

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

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

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

Каркас коллекций 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();

Нуль не является "проблемой". Это неотъемлемая часть полного набора инструментов моделирования. Программное обеспечение стремится моделировать сложность мира и ноль несет свое бремя. Нуль означает "Нет данных" или "Неизвестно" в 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;
        }
    }
    

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

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

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

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

    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 для каждого необработанного исключения, которое фактически является ошибкой приложения. Программист должен исправить ошибку где-то в инициализации.

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

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

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

Действительно распространенная "проблема" в 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 если какой-либо из параметров является нулевым (отмечен или не отмечен, это скорее проблема дизайна / вкуса, но мой ValidationException проверено).

void validate() throws ValidationException;

Сообщение будет содержать следующий текст, если, например, "plan" равно нулю:

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

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

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

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

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

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

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

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

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

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

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

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



    .....
}

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

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

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

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

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

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

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

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

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

В двух словах, в классе 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, а также нулевой код без проверки... Правда?,

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

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

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

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

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

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

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

Байт!

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

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

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

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

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

}

или же

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

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

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

   // ...
}

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

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

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

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

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

  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.

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

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

Подход 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;
}

Java 8 представила новый класс Optional в пакете java.util.

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

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

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

Более подробную информацию вы найдете здесь: oracle docs: - https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

Другие вопросы по тегам