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

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

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

Например:

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

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

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

Ответы [ 62 ]

4 голосов
/ 05 июля 2016

В 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 голосов
/ 04 июля 2017

Если вы используете java8 или более позднюю версию, выберите isNull(yourObject) из java.util.Objects.

Пример: -

String myObject = null;

Objects.isNull(myObject); //will return true
3 голосов
/ 16 декабря 2015

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

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

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

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

2 голосов
/ 14 июля 2012

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

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

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

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

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

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

  1. Он может "ИМЕТЬ" учеников, и они будут следовать этим правилам:

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

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

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

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

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

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

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

2 голосов
/ 28 января 2014

Способ избежать ненужного 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 -значений, это может быть признаком плохо спроектированного интерфейса, который требует рефакторинга / дополнений в suite потребности клиентов.

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

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

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

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

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

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "null";
}
2 голосов
/ 07 февраля 2018

С 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();
        }
    }
2 голосов
/ 10 января 2013

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

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

ObjectUtils.equals(object, null)

или

CollectionUtils.isEmpty(myCollection);

или

StringUtils.isEmpty("string");

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

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

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

1 голос
/ 12 сентября 2012

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

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

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

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