Как избежать проверки нулевых значений в цепочке методов?
Мне нужно проверить, является ли какое-то значение нулевым или нет. И если его не 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 выполняет всю нулевую проверку...)
Ответ 6
Вы также можете посмотреть vavr Option, который, как описано ниже, лучше, чем Java Optional и имеет гораздо более богатый API.
https://softwaremill.com/do-we-have-better-option-here/