Извлечение значения без проверки нуля в Java

15

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

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

public final class NoNPE {

    public static <T> T get(NoNPEInterface<T> in) {
        try {
            return in.get();
        } catch (NullPointerException e) {
            return null;
        }
    }

    public interface NoNPEInterface<T> {
        T get();
    }
}

Я использую это немного как это ...

Room room = NoNPE.get(() -> country.getTown().getHouses().get(0).getLivingRoom());

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

Что вы думаете о вышесказанном? Я создаю проблемный образец? Есть ли лучший способ сделать это по вашему мнению?

Эуриг Джонс
источник
1
Поскольку вы, очевидно, используете Java 8, могу ли я предложить вам изменить дизайн своего приложения, чтобы использовать java.util.Optionalвместо нулей для представления отсутствующих данных? Это предоставляет удобные утилиты как для описываемого вами случая, так и для случаев, когда вы хотите продолжить работу с данными по умолчанию, а не просто возвращать условие сбоя в конце цепочки ..
Periata Breatta
Я думаю, что вы по сути заново открыли Option(или Maybe) монаду :)
Андрес Ф.
Может быть возможным вернуть Optional вместо T или null: таким образом, вы можете использовать метод orElse () напрямую. 18 месяцев спустя, но мог бы помочь кому-то.
Бендж
В этом посте упоминаются и другие подходы нелегально.arlogspot.com/2015/03/… , один из которых использует библиотеку с именем kludje, которая имеет очень интересный синтаксис
Benj

Ответы:

13

Ваше решение очень умное. Проблема, которую я вижу, в том, что вы не знаете, почему у вас есть null? Это потому, что в доме не было комнат? Было ли это потому, что в городе не было домов? Это потому, что в стране не было городов? Было ли это из- nullза ошибки в позиции 0 из-за ошибки, даже если дома находятся в позициях 1 и выше?

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

Кроме того, это нарушает закон Деметры : country.getTown().getHouses().get(0).getLivingRoom(). Чаще всего нарушение какого-то хорошего принципа заставляет вас искать неортодоксальные решения для решения проблемы, вызванной нарушением такого принципа.

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

Тулаинс Кордова
источник
Отличный ответ. Да, я не буду знать, где я получил ноль в цепи. Во многих случаях, хотя мне все равно, отсутствие проверки нуля означает, что код более читабелен и менее подвержен ошибкам. Но да, вы правы в некоторых случаях, когда мне нужно принять другое логическое решение, если родительский объект является нулевым, тогда это может вызвать проблемы. Обычный метод или необязательный класс могут быть там более безопасным решением.
Эуриг Джонс
В общем, когда вы используете Optionмонаду, вам все равно, где в цепочке отсутствует значение. Когда вы заботитесь об этом, вы, вероятно, используете другой тип, например Either.
Андрес Ф.
Подход ФП аналогичен C # 6 ?.и ?[]операторам. Одним из примеров того, когда вы можете захотеть использовать такую ​​вещь, являются иерархические настройки на стороне сервера. var shouldDoThing = settings?.a?.b?.c ?? defaultSetting;Кого волнует, почему какая-то часть этого была нулевой? Может быть, вы не смогли получить настройки. Может быть, вы решили удалить раздел настроек. В любом случае, вы никогда не сможете рассчитывать на получение настроек сервера, поэтому по умолчанию это хорошая идея, и вам вряд ли интересно, почему вы не можете получить фактические настройки, если это не происходит очень часто, когда этого не следует делать. ,
Крис
Теперь я не говорю, что это строго лучше или хуже, чем локализация значений по умолчанию и просто возвращение желаемого значения с помощью обычного доступа settings.a.b.c. Опять же, это единственный изолированный пример.
Крис
10

Идея хорошая, действительно хорошая на самом деле. Поскольку в Java 8 Optionalсуществуют типы, подробное объяснение можно найти в дополнительном типе Java . Пример с тем, что вы опубликовали

Optional.ofNullable(country)
    .map(Country::getTown)
    .map(Town::Houses);

И дальше.

Дж. Пичардо
источник
1
Да, я знал о классе Optional из Java 8 и Guava, и они действительно полезны. Но вы не можете просто получить объект, так как вы обычно делаете код немного сложнее для чтения и менее производительным. Но плюс в том, что есть много очень полезных операторов, которые предоставляет класс Optional.
Эуриг Джонс
3
@EurigJones Я не думаю, что код становится менее производительным. Читаемость в глазах смотрящего, но я бы поспорил, Optionalявляется более читабельным решением из двух, хотя бы потому, что - в отличие от вашего предложения - это очень распространенная идиома. Это даже более кратко, чем у вас!
Андрес Ф.
0

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

Старайтесь избегать nulls, когда вы можете, и передавать их только тогда, когда они представляют что-то или имеют особое значение, и возвращать их только тогда, когда они представляют / значат что-то, иначе вы должны бросить a NullPointerException. Это позволяет избежать ошибок и путаницы. Если не Objectдолжно быть null, NullPointerдолжно быть брошено. Если объект может быть, nullтогда ничто не пойдет не так, когда его передадут. В противном случае ваш метод работает выше.

Люк Мелая
источник
0

Я чувствую вашу боль, но предложенное решение - плохая идея.

  • Если один из получателей бросает NPE по какой-либо другой причине, вы его проигнорируете.
  • Есть риск, что эта внутренняя лямбда превратится в ужасный код. Например, если есть новое требование возвращать специальную константу, когда в городе нет домов, ленивый программист может расширить lamda, оставив все в оболочке NoNPE.get.
  • Как уже упоминалось, Optional.mapэто то , что вы ищете.
  • Наказание за создание нового экземпляра NullPointerException часто является значительным. Это много микросекунд, особенно когда ваш стек вызовов становится больше. Трудно предсказать, где ваша утилита будет использоваться.

Как примечание стороны, NoNPEInterfaceявляется дубликатом java.util.function.Supplier.

В некоторых случаях вы можете рассмотреть возможность использования утилит для оценки выражений, которые присутствуют во многих средах (например, EL, SpEL):

evaluateProperty(country, "town.houses[0].livingRoom")
Матеуш Стефек
источник
Хорошо для шаблонов веб-страниц, но обычно медленно развивается (без проверки времени компиляции) и медленно запускается.
Кевин Клайн