Безопасно получить значение из цепочки без NPE - PullRequest
0 голосов
/ 17 января 2020

Кто-нибудь знает какое-либо решение для безопасного получения стоимости без NPE и без множества if утверждений? Например, у нас есть: userInfo.getAddressInfo().getCityName(), как получить cityName без нулевых проверок?

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

public static <T> String safeGetValue(Supplier<String> supplier) {
    try {
        return supplier.get();
    } catch (NullPointerException e) {
        e.printStackTrace();
    }
    return "";
}

Возможно существует лучший способ сделать это.

Полный пример:

import java.util.function.Supplier;

public class ExampleClass {
    public static void main(String[] args) {
        UserInfoResponse userInfo = new UserInfoResponse();

        String value = safeGetValue(() -> userInfo.getAddressInfo().getCityName());
        System.out.println(value);
    }

    public static <T> String safeGetValue(Supplier<String> supplier) {
        try {
            return supplier.get();
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static <T> String safeGetValue(Supplier<String> supplier, String defaultValue) {
        try {
            return supplier.get();
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
        return defaultValue;
    }

    public static <T> String safeGetValue(Supplier<String> supplier, Runnable runnable) {
        try {
            return supplier.get();
        } catch (NullPointerException e) {
            runnable.run();
        }
        return "";
    }

    public static <T> String safeGetValue(Supplier<String> supplier, Runnable runnable, String defaultValue) {
        try {
            return supplier.get();
        } catch (NullPointerException e) {
            runnable.run();
        }
        return defaultValue;
    }
}

class UserInfoResponse {

    private String firstName;

    private UserAddressInfo addressInfo;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public UserAddressInfo getAddressInfo() {
        return addressInfo;
    }

    public void setAddressInfo(UserAddressInfo addressInfo) {
        this.addressInfo = addressInfo;
    }
}

class UserAddressInfo {

    private String cityName;

    public String getCityName() {
        return cityName;
    }

    public void setCityName(String cityName) {
        this.cityName = cityName;
    }
}

Ответы [ 2 ]

3 голосов
/ 17 января 2020

Я ищу решение без операторов if.

Тернарное утверждение тоже не может быть хорошим решением.

К сожалению, if тесты и условные выражения являются единственными альтернативами отлову NPE. (Но они - лучшие альтернативы!)


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

Вот несколько способов, с помощью которых ваши API-интерфейсы не должны возвращать null.

  • Сделать невозможным создание объектов домена со значениями поля null:

    • Конструкторы и сеттеры должны проверять, что их аргументы не равны NULL, и вызывать исключение (например, NPE) при вызове с фиктивными null аргументами.
    • Поля по умолчанию могут быть инициализированы с ненулевым значения.
  • Пусть получатели возвращают ненулевые значения, когда значения поля null. (Но см. Ниже!)

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

  • Используйте шаблон Null Object . Создайте специальный экземпляр каждого из ваших доменных объектов, который представляет «нет объекта». В идеале, нулевой объект должен быть неизменным ... или должен выдавать исключение, если вы попытаетесь изменить его случайно.

  • Используйте Optional<ReferenceType>.

  • Для встроенных типов / общих типов:

    • используйте вместо этого "" null для строк
    • использовать массив нулевой длины вместо null для массивов
    • использовать (неизменяемые) пустые объекты коллекции вместо null для типов коллекции
    • Избегайте примитивных типов-оболочек: используйте примитивные типы, которые не могут быть null.

Другой способ взглянуть на это состоит в том, что если указан ваш API так что получатель не должен возвращать null, тогда, если он возвращает null , это ошибка . Итак, если вы затем напишите код, чтобы превратить пустые значения во что-то другое (например, пустые строки), чтобы избежать надоедливых NPE, то, что вы на самом деле делаете, это , скрывая ошибки . Лучшая идея - позволить NPE произойти, зарегистрировать его, а затем создать sh приложение. (Или верните ответ «500 Internal Error».)

Исправить ошибку лучше, чем скрыть ошибку.

Но (я слышал, вы спрашиваете) «Как насчет надежности? это смущает, если мой код падает с NPE! "

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

И общепринятым способом избежать ошибок (например, NPE), появляющихся в производстве, является обширное автоматизированное тестирование. Больше / лучше юнит тестов. Больше / лучше системных тестов. Бета-тестовые выпуски, опытные серверы и т. Д. c.

1 голос
/ 17 января 2020

Используя Optional<T> цепочку, вы можете написать следующее

UserInfoResponse userInfo = new UserInfoResponse();

String value = Optional.ofNullable(userInfo.getAddressInfo())
        .map(UserAddressInfo::getCityName)
        .orElse(null);

или

String value = Optional.ofNullable(userInfo)
        .map(UserInfoResponse::getAddressInfo)
        .map(UserAddressInfo::getCityName)
        .orElse(null);

или

String value = Optional.ofNullable(userInfo)
        .map(ui -> ui.getAddressInfo())
        .map(uai -> uai.getCityName())
        .orElse(null);
...