Недавно мы перешли на Java 8. Теперь я вижу приложения, заполненные Optional
объектами.
До Java 8 (Стиль 1)
Employee employee = employeeServive.getEmployee();
if(employee!=null){
System.out.println(employee.getId());
}
После Java 8 (Стиль 2)
Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
Employee employee = employeeOptional.get();
System.out.println(employee.getId());
}
Я не вижу добавленной стоимости, Optional<Employee> employeeOptional = employeeService.getEmployee();
когда сама служба возвращает необязательно:
Исходя из фона Java 6, я вижу Стиль 1 как более понятный и с меньшим количеством строк кода. Есть ли какое-то реальное преимущество, которого мне здесь не хватает?
Консолидирован от понимания всех ответов и дальнейших исследований в блоге
employeeOptional.isPresent()
кажется, полностью упускает из виду типы опций. Согласно комментарию @ MikePartridge, это не рекомендуется или идиоматично. Явная проверка, является ли тип параметра чем-то или ничем, является нет-нет. Вы должны простоmap
илиflatMap
над ними.Optional
Ответы:
Стиль 2 не подходит для Java 8, чтобы увидеть все преимущества. Вы не хотите
if ... use
вообще. Смотрите примеры Oracle . Принимая их советы, мы получаем:Стиль 3
Или более длинный фрагмент
источник
void ifPresent(Consumer<T>, Runnable)
я бы поспорил о полном запретеisPresent
иget
ifPresentOrElse(Consumer<? super T>, Runnable)
.Если вы используете
Optional
в качестве слоя «совместимости» старый API, который все еще может возвращатьсяnull
, может быть полезно создать (не пустой) Optional на последнем этапе, когда вы уверены, что у вас что-то есть. Например, где вы написали:Я бы выбрал:
Дело в том, что вы знаете, что есть служба , отличная от NULL , поэтому вы можете завершить ее
Optional
с помощьюOptional.of()
. Затем, когда вы звонитеgetEmployee()
по этому поводу, вы можете или не можете получить сотрудника. Этот сотрудник может (или, возможно, не может) иметь удостоверение личности. Затем, если у вас есть идентификатор, вы хотите его распечатать.Там нет необходимости явно проверить любой нуль, наличие и т.д., в этом коде.
источник
(...).ifPresent(aMethod)
болееif((...).isPresent()) { aMethod(...); }
? Кажется, это просто еще один синтаксис для выполнения почти той же операции.Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet);
Теперь я могу обрабатыватьchildren
равномерно, например,children.forEach(relative::sendGift);
. Они могут даже быть объединеныOptional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);
. Весь шаблон проверки нуля и т. Д.,Существует почти нет добавленной стоимости в наличии
Optional
одного значения. Как вы видели, это просто заменяет проверку наnull
проверку на наличие.Существует огромное преимущество в том, чтобы иметь
Collection
такие вещи. Все методы потоковой передачи, введенные для замены низкоуровневых циклов старого стиля, понимаютOptional
вещи и делают с ними правильные вещи (не обрабатывают их или, в конечном счете, возвращают еще одно незаданноеOptional
). Если вы имеете дело с n предметами чаще, чем с отдельными предметами (а 99% всех реалистичных программ это делают), то иметь встроенную поддержку для опционально представляемых вещей - огромное преимущество. В идеальном случае вам никогда не придется проверять наличиеnull
или наличие.источник
Employee getEmployee()
противOptional<Employee> getEmployee()
. Хотя технически оба могут вернутьсяnull
, один из них может вызвать проблемы..map()
без необходимости проверять на ноль все это прекрасно. Например,Optional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);
.null
.Пока вы используете
Optional
просто как модный API дляisNotNull()
, то да, вы не найдете никаких различий только с проверкойnull
.Вещи, которые вы НЕ должны использовать
Optional
дляOptional
Bad Code ™ используется только для проверки наличия значения:Вещи, которые вы должны использовать
Optional
дляИзбегайте отсутствия значения, создавая его при необходимости при использовании методов API с нулевым возвратом:
Или, если создание нового сотрудника слишком дорого:
Кроме того, чтобы все знали, что ваши методы могут не возвращать значение (если по какой-либо причине вы не можете вернуть значение по умолчанию, как описано выше):
И, наконец, есть все потоковые методы, которые уже были объяснены в других ответах, хотя на самом деле вы решили не использовать их,
Optional
а использоватьOptional
предоставленные кем-то другим.источник
Optional
упущенную возможность. «Вот, сделайте эту новуюOptional
вещь, которую я сделал, чтобы вы были вынуждены проверять нулевые значения. О, и, кстати ... Я добавилget()
метод, чтобы вам на самом деле не нужно ничего проверять;);)» , (...)Optional
хорош как неоновая вывеска «ЭТО МОЖЕТ БЫТЬ НУЛЬ», но само существование егоget()
метода наносит ему вред » . Но OP спрашивал причины использованияOptional
, а не причины против этого или недостатки дизайна.Employee employee = Optional.ofNullable(employeeService.getEmployee());
В вашем третьем фрагменте не должно быть типаOptional<Employee>
?get
заключается в том, что вам нужно взаимодействовать с каким-либо устаревшим кодом. Я не могу придумать много веских причин в новом коде когда-либо использоватьget
. Тем не менее, при взаимодействии с унаследованным кодом часто не существует альтернативы, но, тем не менее, Уален считает, что существованиеget
побуждает новичков писать плохой код.Map
разу не упомянул, и я не знаю никого, кто бы делал такие радикальные изменения, не совместимые с предыдущими версиями , поэтому уверен, что вы можете намеренно создавать плохие API-интерфейсыOptional
- не уверен, что это доказывает. ЭтоOptional
имело бы смысл для некоторогоtryGet
метода все же.Map
- это пример, с которым все знакомы. Конечно, они не могут сделатьMap.get
возвращение Optional из-за обратной совместимости, но вы можете легко представить другой класс, похожий на Map, у которого не было бы таких ограничений совместимости. Скажиclass UserRepository {Optional<User> getUserDetails(int userID) {...}}
. Теперь вы хотите получить данные пользователя, вошедшего в систему, и знаете, что они существуют, потому что им удалось войти в систему, и ваша система никогда не удаляет пользователей. Так и делаешьtheUserRepository.getUserDetails(loggedInUserID).get()
.Для меня ключевым моментом использования Optional является ясность, когда разработчику необходимо проверить, возвращает ли функция возвращаемое значение или нет.
источник
Ваша главная ошибка в том, что вы все еще мыслите более процедурно. Это не критика вас как личности, это просто наблюдение. Мышление в более функциональных терминах приходит со временем и практикой, и поэтому методы isPresent и выглядят как наиболее очевидные правильные вещи для вас. Ваша второстепенная незначительная ошибка - создание вашего Optional внутри вашего метода. Необязательный предназначен для того, чтобы помочь документировать, что что-то может или не может возвращать значение. Вы можете ничего не получить.
Это привело к тому, что вы пишете совершенно разборчивый код, который кажется вполне разумным, но вас соблазнили мерзкие двойники-соблазнители, которые получают и представляют.
Конечно, быстро возникает вопрос: «Почему они присутствуют и даже там?»
Многие люди здесь упускают из виду то, что isPresent () состоит в том, что это не то, что сделано для нового кода, написанного полностью занятыми людьми с тем, насколько чертовски полезны лямбды и кому нравится функциональность.
Это, однако, дает нам несколько (два) хороших, великих, гламурных (?) Преимуществ:
Первый довольно прост.
Представьте, что у вас есть API, который выглядел так:
И у вас был унаследованный класс, использующий это как таковой (заполните пробелы):
Придуманный пример, чтобы быть уверенным. Но потерпите меня здесь.
Java 8 уже запущена, и мы карабкаемся, чтобы попасть на борт. Таким образом, мы хотим заменить наш старый интерфейс чем-то, что возвращает Optional. Почему? Потому что, как кто-то другой уже любезно упомянул: это снимает догадки о том, может ли что-то быть нулевым. Это уже было указано другими. Но сейчас у нас проблема. Представьте, что у нас есть (извините, когда я нажимаю alt + F7 невинного метода) 46 мест, где этот метод вызывается в хорошо проверенном унаследованном коде, который отлично работает в противном случае. Теперь вы должны обновить все это.
Это где настоящее сияет.
Потому что сейчас: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {throw new NoSuchSnickersException (); } else {consumer.giveDiabetes (lastSnickers); }
будет выглядеть так:
И это простое изменение, которое вы можете дать новому юниору: он может сделать что-то полезное, и он сможет одновременно исследовать кодовую базу. беспроигрышная. В конце концов, что-то похожее на этот шаблон довольно широко распространено. И теперь вам не нужно переписывать код, чтобы использовать лямбды или что-то еще. (В этом конкретном случае это было бы тривиально, но я оставляю продуманные примеры, где это было бы сложно в качестве упражнения для читателя.)
Обратите внимание, что это означает, что то, как вы это сделали, по сути является способом работы с устаревшим кодом без дорогостоящих переписываний. Так что насчет нового кода?
Ну, в вашем случае, когда вы просто хотите что-то напечатать, вы просто сделаете:
. SnickersCounter.lastConsumedSnickers () ifPresent (System.out :: Println);
Что довольно просто и совершенно понятно. Точка, которая медленно поднимается к поверхности, состоит в том, что существуют варианты использования для get () и isPresent (). Они предназначены для того, чтобы позволить вам механически модифицировать существующий код, чтобы использовать новые типы, не слишком задумываясь об этом. То, что вы делаете, поэтому вводится в заблуждение следующими способами:
Если вы хотите использовать Optional в качестве простой проверки нулевой безопасности, вам нужно просто выполнить следующее:
Конечно, красивая версия выглядит так:
Кстати, хотя это ни в коем случае не требуется, я рекомендую использовать новую строку для каждой операции, чтобы ее было легче читать. Легко читать и понимать бьет краткость в любой день недели.
Это, конечно, очень простой пример, в котором легко понять все, что мы пытаемся сделать. Это не всегда так просто в реальной жизни. Но обратите внимание, что в этом примере мы выражаем наши намерения. Мы хотим ПОЛУЧИТЬ сотрудника, ПОЛУЧИТЬ его удостоверение личности и, если возможно, распечатать его. Это вторая большая победа с Факультативным. Это позволяет нам создавать более понятный код. Я также думаю, что создание таких вещей, как создание метода, который делает кучу вещей, чтобы вы могли передать их на карту, в общем, хорошая идея.
источник
map(x).ifPresent(f)
просто не имеет такого же интуитивного смысла, какif(o != null) f(x)
это.if
Версия совершенно ясно. Это еще один случай, когда люди прыгают в популярном фургоне, а не в случае реального прогресса.Optional
. Я имел в виду только использование необязательного в данном конкретном случае и, более того, читабельность, поскольку несколько человек действуют так, что этот формат одинаково или более удобочитаем, чемif
.isPresent
иget
насколько это реально возможно), они улучшить безопасность . Труднее написать неправильный код, который не может проверить отсутствие значения, если тип является типом,Optional<Whatever>
а не обнуляемымWhatever
.get
, вы вынуждены выбирать, что делать, когда происходит пропущенное значение: вы либо используетеorElse()
(илиorElseGet()
), чтобы предоставить значение по умолчанию, либоorElseThrow()
выбросить выбранное значение. исключение, которое документирует природу проблемы , которая лучше, чем общий NPE, который не имеет смысла, пока вы не копаетесь в трассировке стека.Я не вижу преимущества в стиле 2. Вы все еще должны понимать, что вам нужна нулевая проверка, и теперь она еще больше, а значит, менее читабельна.
Лучший стиль был бы, если employeeServive.getEmployee () вернул бы Optional, и тогда код стал бы:
Таким образом, вы не можете вызвать callemployeeServive.getEmployee (). GetId (), и вы заметили, что вам следует каким-то образом обрабатывать необязательные значения. И если во всей вашей кодовой базе методы никогда не вернут null (или почти никогда с хорошо известными вашей команде исключениями из этого правила), это повысит безопасность от NPE.
источник
isPresent()
. Мы используем опционально, поэтому нам не нужно тестировать.isPresent()
. Это неправильный подход к опциям. Используйтеmap
илиflatMap
вместо.ifPresent
) это просто еще один способ проверить.ifPresent(x)
- это такой же тест, как иif(present) {x}
. Возвращаемое значение не имеет значения.Я думаю, что наибольшим преимуществом является то, что если сигнатура вашего метода возвращает,
Employee
вы не проверяете на ноль. По этой подписи вы знаете, что гарантированно вернуть работника. Вам не нужно обрабатывать случай отказа. С опциональной вы знаете, что вы делаете.В коде, который я видел, есть нулевые проверки, которые никогда не будут терпеть неудачу, так как люди не хотят прослеживать код, чтобы определить, возможен ли нулевой результат, поэтому они бросают нулевые проверки везде с защитой. Это делает код немного медленнее, но, что более важно, делает его намного шумнее.
Однако, чтобы это работало, вы должны последовательно применять этот шаблон.
источник
@Nullable
и@ReturnValuesAreNonnullByDefault
вместе со статическим анализом.package-info.java
со@ReturnValuesAreNonnullByDefault
всеми моими пакетами, поэтому все, что мне нужно сделать, это добавить,@Nullable
куда вы должны переключитьсяOptional
. Это означает меньшее количество символов, отсутствие добавленного мусора и никаких накладных расходов во время выполнения. Мне не нужно искать документацию, как показано при наведении указателя мыши на метод. Инструмент статического анализа улавливает ошибки и последующие изменения обнуляемости.Optional
Больше всего меня беспокоит то, что это добавляет альтернативный способ борьбы с обнуляемостью. Если это было в Java с самого начала, это может быть хорошо, но сейчас это просто боль. Идти по пути Котлина было бы ИМХО намного лучше. +++ Обратите внимание, чтоList<@Nullable String>
по-прежнему список строк, аList<Optional<String>>
нет.Мой ответ: просто не надо. По крайней мере, дважды подумайте, действительно ли это улучшение.
Выражение (взято из другого ответа) как
похоже, это правильный функциональный способ иметь дело с изначально обнуляемыми возвращаемыми методами. Это выглядит красиво, никогда не бросает и никогда не заставляет задуматься о «отсутствующем» случае.
Это выглядит лучше, чем в старом стиле
что делает очевидным, что могут быть
else
оговорки.Функциональный стиль
orElse
не всегда достаточно)Ни в коем случае его не следует использовать как панацею. Если это было универсально применимо, то семантика
.
оператора должна быть заменена семантикой?.
оператора , NPE забыт и все проблемы проигнорированы.Будучи большим поклонником Java, я должен сказать, что функциональный стиль - просто замена плохого человека на Java.
хорошо известен из других языков. Согласны ли мы с этим утверждением?
источник
employeeService.getEmployee().getId().apply(id => System.out.println(id));
и сделайте его нуль-безопасным один раз с помощью оператора безопасной навигации и один раз с помощьюOptional
. Конечно, мы можем назвать это «деталью реализации», но реализация имеет значение. Есть случаи, когда lamdas делают код проще и приятнее, но здесь это просто уродливое злоупотребление.Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);
функция Язык:employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));
. Два синтаксиса, но в итоге они выглядят очень похоже на меня. И "концепция" это такжеOptional
известныйNullable
какMaybe
+++
То, что подвергается насилию, - это лямды для обработки обнуляемости Ламды в порядке, ноOptional
это не так. Nullability просто нуждается в правильной языковой поддержке, как в Kotlin. Другая проблема:List<Optional<String>>
не связана с темList<String>
, где нам нужно, чтобы они были связаны.Необязательным является понятие (тип более высокого порядка), исходящее из функционального программирования. Использование этого избавляет от вложенной проверки нуля и спасает вас от пирамиды гибели.
источник