Должен ли я использовать Java8/Guava Необязательный для каждого метода, который может возвращать ноль?
Необязательный используется для представления обнуляемого объекта. Некоторые применения этого класса включают
- В качестве метода, возвращающего тип, в качестве альтернативы для возврата нуля
указать, что никакое значение не было доступно - Различать "неизвестно" (например, не присутствует на карте) и "известно, что не имеет значения" (присутствует на карте со значением
Optional.absent()) - Обернуть пустые ссылки для хранения в коллекции, которая не поддерживает null (хотя есть несколько других подходов к этому, которые следует рассмотреть в первую очередь)
В первом случае нужно ли возвращать Optional во всех методах возврата, допускающих значение NULL?
3 ответа
Так что не так с дополнительным?
Вопрос, с которым мы сталкиваемся: избавится ли от необязательных объектов JDK 8 от нулевых ссылок? И ответ категорически нет! Таким образом, хулители сразу же ставят под сомнение его ценность, спрашивая: что же хорошего в этом, чего мы не могли сделать другими способами?
В отличие от функциональных языков, таких как SML или Haskell, в которых никогда не было концепции нулевых ссылок, в Java мы не можем просто избавиться от нулевых ссылок, которые исторически существовали. Это будет продолжать существовать, и они, возможно, имеют свое надлежащее использование (просто чтобы упомянуть пример: трехзначная логика).
Я сомневаюсь, что намерение с классом Optional состоит в том, чтобы заменить каждую ссылку, допускающую значение NULL, но помочь в создании более надежных API, в которых, просто читая сигнатуру метода, мы можем сказать, можем ли мы ожидать необязательное значение или нет, и заставить программиста использовать это значение соответственно. Но, в конечном счете, Optional будет просто еще одной ссылкой и подчиняется тем же слабостям, что и любая другая ссылка в языке (например, вы можете вернуть ноль Optional). Совершенно очевидно, что Optional не собирается спасать день.
То, как эти необязательные объекты должны использоваться или являются ли они ценными или нет в Java, было предметом горячих споров в лямбда-рассылке проекта. От хулителей мы слышим интересные аргументы, такие как:
- Факт, что существуют другие альтернативы (например, IDES, такие как IntelliJ и Eclipse IDE, поддерживают набор проприетарных аннотаций для статического анализа обнуляемости, JSR-305 с аннотациями, такими как @Nullable и @NonNull).
- Некоторые хотели бы, чтобы он был применим как в функциональном мире, что не совсем возможно в Java, так как в языке отсутствуют многие функции, существующие в функциональных языках программирования, таких как SML или Haskell (например, сопоставление с образцом).
- Другие спорят о том, как невозможно модифицировать существующий код, чтобы использовать эту идиому (например, List.get(Object), который будет продолжать возвращать ноль).
- А некоторые жалуются на тот факт, что отсутствие языковой поддержки для необязательных значений создает потенциальный сценарий, в котором Optional может быть непоследовательно использован в API, что создает несовместимости, почти такие же, как те, которые мы будем иметь с остальным API Java. который не может быть модернизирован для использования нового дополнительного класса. Это в значительной степени ваш вопрос здесь. В языках с поддержкой необязательных типов, таких как Цейлон или Kotlin, вы бы даже не сомневались в этом.
- Убедительным аргументом является то, что если программист вызывает метод get в необязательном объекте, если он пуст, он вызовет исключение NoSuchElementException, что в значительной степени та же проблема, что и у нас с нулями, только с другим исключением.
Таким образом, может показаться, что преимущества Optional действительно сомнительны и, вероятно, ограничены улучшением читабельности и обеспечением выполнения контрактов с открытым интерфейсом.
Я действительно считаю, что принятие этой дополнительной функциональной идиомы, вероятно, сделает наш код более безопасным, менее быстрым для устранения проблем разыменования и, как следствие, более устойчивым и менее подверженным ошибкам. Конечно, это не идеальное решение, потому что, в конце концов, необязательные ссылки также могут быть ошибочно установлены на нулевые ссылки, но я ожидаю, что программисты придерживаются соглашения не передавать нулевые ссылки, где ожидается необязательный объект, в значительной степени, как сегодня мы считаем хорошей практикой не передавать нулевую ссылку, где ожидается коллекция или массив, в этих случаях правильным будет пропуск пустого массива или коллекции. Суть в том, что теперь у нас есть механизм в API, который мы можем использовать, чтобы сделать явным, что для данной ссылки у нас может не быть значения, чтобы назначить его, и пользователь вынужден, API, проверить это.
Цитирую статью Google Guava об использовании дополнительных объектов:
"Помимо повышения читабельности за счет присвоения нулевому имени, самым большим преимуществом Optional является его защищенность от идиотов. Это заставляет вас активно думать об отсутствующем кейсе, если вы хотите, чтобы ваша программа вообще компилировалась, так как вы должны активно разворачивать Optional и решать этот кейс ".
Итак, я думаю, что каждый разработчик API должен выбрать, насколько далеко он хочет пойти в использовании Optional.
Некоторые влиятельные разработчики, такие как Стивен Колебурн и Брайан Гетц, недавно опубликовали несколько интересных статей о правильном использовании опциональных устройств. Я особенно нашел полезным следующее:
В качестве наблюдения я думаю, что одним из наиболее важных аспектов, которые необходимо учитывать при разработке приложения, является решение, как рассматривать "нулевую проблему". В этом отношении первым шагом будет определение возможных "источников" нулей. Например, база данных или внешние библиотеки, используемые в проекте. Следующим шагом было бы "сдержать" проблему, а именно, обернуть проблемный код (используя Optional) и, таким образом, заблокировать распространение нуля по всей системе, где ничего не подозревающий код может вызвать NPE.
Чтобы ответить на ваш вопрос, это зависит... В большинстве случаев я бы сказал, что в этом нет необходимости, поскольку это создает много работы без значения (например, я бы не использовал опционально для методов, которые вызывают другие частные методы внутри классов, или для методов, которые вызывают методы закрытых для пакета классов), но код, который существует на тонкой границе различных "проблем" (или уровней) в вашем приложении (сигнатура интерфейсов / классов, которые вы используете для запроса к хранилищу данных, например, или pojos, используемые для "транспортировки" данных, которые могут иметь нулевые свойства (DTO, или, более общее описание, опубликованные API различных модулей), следует избегать "утечки" нулей в некоторые другие области с различными проблемами.
По сравнению с гуавой, одна досадная проблема с java.util.Optional
является то, что он не обеспечивает такой метод, как
orElse(Optional<T>): Optional<T>
который, с другой стороны, определен в com.google.common.base.Optional
как
or(Optional<T>): Optional<T>
Отсутствие этой специфической функции ограничивает монадические приложения Java 8 по выбору.
ОБНОВИТЬ:
Гуава-х or(Optional<T>)
может быть воспроизведен как в Java 8
optionalA.map(Optional::of).orElse(optionalB)
или же
optionalA.map(Optional::of).orElseGet(() -> computeOptionalB)
ОБНОВИТЬ:
В Java 9 (наконец-то!) Вы сможете использовать Optional.or(Supplier<Optional<T>>)
:
optionalA.or(() -> computeOptionalB)
Это аккуратно!