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

Мне нужно проверить, является ли какое-то значение нулевым или нет. И если его не null, то просто установите некоторую переменную в true. Здесь нет никакого заявления. У меня слишком много проверок состояния.

Есть ли способ справиться с этими нулевыми проверками без проверки всех возвращаемых значений метода?

if(country != null && country.getCity() != null && country.getCity().getSchool() != null && country.getCity().getSchool().getStudent() != null .....) {
    isValid = true;
}

Я думал, что прямо проверяет переменную и игнорирует исключение NullpointerException. Это хорошая практика?

try{
    if(country.getCity().getSchool().getStudent().getInfo().... != null)
} catch(NullPointerException ex){
    //dont do anything.
}

Ответы

Ответ 1

Нет, в Java, как правило, не очень хорошая практика поймать NPE вместо нулевой проверки ваших ссылок.

Вы можете использовать Optional для этого типа, если хотите:

if (Optional.ofNullable(country)
            .map(Country::getCity)
            .map(City::getSchool)
            .map(School::getStudent)
            .isPresent()) {
    isValid = true;
}

или просто

boolean isValid = Optional.ofNullable(country)
                          .map(Country::getCity)
                          .map(City::getSchool)
                          .map(School::getStudent)
                          .isPresent();

если это все, что isValid предполагается проверка.

Ответ 2

Вы можете использовать Optional здесь, но на каждом шаге он создает один дополнительный объект.

boolean isValid = Optional.ofNullable(country)
    .map(country -> country.getCity()) //Or use method reference Country::getCity
    .map(city -> city.getSchool())
    .map(school -> school.getStudent())
    .map(student -> true)
    .orElse(false);

//OR
boolean isValid = Optional.ofNullable(country)
                      .map(..)
                      ....
                      .isPresent();

Ответ 3

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

public boolean isValid() {
  return city != null && city.isValid();
}

Это предполагает, что валидация везде одинакова во всей вашей стране, но обычно это так. Если нет, метод следует называть hasStudent(), но это менее общее, и вы рискуете дублировать весь интерфейс School в стране. Например, в другом месте вам может понадобиться hasTeacher() или hasCourse().

Другой подход заключается в использовании нулевых объектов:

public class Country {
  public static final Country NO_COUNTRY = new Country();

  private City city = City.NO_CITY;

  // etc.
}

Я не уверен, что предпочтительнее этот случай (строго вам понадобится подкласс для переопределения всех методов модификации), путь Java 8 должен состоять в том, чтобы пойти с дополнительным как метод в других ответах, но я бы предложил его охватить более полно:

private Optional<City> city = Optional.ofNullable(city);

public Optional<City> getCity() {
   return city;
}

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

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

Ответ 4

В качестве альтернативы другому прекрасному использованию Optional мы также могли бы использовать метод утилиты с параметром Supplier<Object> var-args в качестве параметра.
Это имеет смысл, поскольку у нас нет большого количества вложенных уровней в объекте для проверки, кроме многих полей для проверки.
Кроме того, его можно легко модифицировать для регистрации/обработки чего-либо при обнаружении null.

    boolean isValid = isValid(() -> address, // first level
                              () -> address.getCity(),   // second level
                              () -> address.getCountry(),// second level
                              () -> address.getStreet(), // second level
                              () -> address.getZip(),    // second level
                              () -> address.getCountry() // third level
                                           .getISO()


@SafeVarargs
public static boolean isValid(Supplier<Object>... suppliers) {
    for (Supplier<Object> supplier : suppliers) {
        if (Objects.isNull(supplier.get())) {
            // log, handle specific thing if required
            return false;
        }
    }
    return true;
}

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

boolean isValid = isValid(  Arrays.asList("address", "city", "country",
                                          "street", "zip", "Country ISO"),
                            () -> address, // first level
                            () -> address.getCity(),   // second level
                            () -> address.getCountry(),// second level
                            () -> address.getStreet(), // second level
                            () -> address.getZip(),    // second level
                            () -> address.getCountry() // third level
                                         .getISO()
                         );


@SafeVarargs
public static boolean isValid(List<String> fieldNames, Supplier<Object>... suppliers) {
    if (fieldNames.size() != suppliers.length){
         throw new IllegalArgumentException("...");
    }
    for (int i = 0; i < suppliers.length; i++) {
        if (Objects.isNull(suppliers.get(i).get())) {
            LOGGER.info( fieldNames.get(i) + " is null");
            return false;
        }
    }
    return true;
}

Ответ 5

Java не имеет "нулевых" операций, таких как, например, Kotlin null safety

Вы также можете:

  • поймать NPE и игнорировать его
  • проверить все ссылки вручную
  • использование Дополнительно согласно другим ответам
  • используйте какой-то инструмент, такой как XLST

В противном случае, если у вас есть контроль над объектами домена, вы можете перепроектировать свои классы, чтобы доступная вам информация была доступна с объекта верхнего уровня (make Country class выполняет всю нулевую проверку...)