Пользуясь Java 8 уже более 6 месяцев, я очень доволен новыми изменениями API. Одна область, в которой я все еще не уверен, это когда использовать Optional
. Я, кажется, колеблюсь между желанием использовать это везде, где-то может быть null
, и нигде вообще.
Кажется, есть много ситуаций, когда я мог бы использовать это, и я никогда не уверен, добавляет ли это преимущества (удобочитаемость / нулевая безопасность) или просто вызывает дополнительные издержки.
Итак, у меня есть несколько примеров, и мне было бы интересно узнать мнение сообщества о том Optional
, полезно ли это.
1 - как открытый тип возвращаемого метода, когда метод может вернуть null
:
public Optional<Foo> findFoo(String id);
2 - В качестве параметра метода, когда параметр может быть null
:
public Foo doSomething(String id, Optional<Bar> barOptional);
3 - В качестве необязательного члена компонента:
public class Book {
private List<Pages> pages;
private Optional<Index> index;
}
4 - в Collections
:
В общем, я не думаю:
List<Optional<Foo>>
добавляет что-нибудь - тем более, что можно использовать filter()
для удаления null
значений и т. д., но есть ли хорошее применение для Optional
в коллекциях?
Любые случаи, которые я пропустил?
Map<Character, String>
. Если нет никакой замены я могу использовать это:Optional.ofNullable(map.get(c)).orElse(String.valueOf(c))
. Также обратите внимание, что Optional был украден из Гуавы, и у него гораздо более приятный синтаксис:Optional.fromNullable(map.get(c)).or(String.valueOf(c));
.filter(Optional::absent)
«getOrDefault()
?Ответы:
Основная задача
Optional
заключается в том, чтобы обеспечить функцию, возвращающую значение, чтобы указать на отсутствие возвращаемого значения. Смотрите это обсуждение . Это позволяет вызывающей стороне продолжать цепочку быстрых вызовов методов.Это наиболее близко соответствует варианту использования № 1 в вопросе OP. Хотя отсутствие значения является более точной формулировкой, чем ноль, поскольку что-то подобное
IntStream.findFirst
никогда не может вернуть ноль.Для варианта использования # 2 , передавая необязательный аргумент методу, это можно сделать работающим, но это довольно неуклюже. Предположим, у вас есть метод, который принимает строку, за которой следует необязательная вторая строка. Принятие в
Optional
качестве второго аргумента приведет к следующему коду:Даже принимать ноль лучше
Вероятно, лучше всего иметь перегруженный метод, который принимает один строковый аргумент и обеспечивает значение по умолчанию для второго:
Это имеет ограничения, но это намного приятнее, чем любой из вышеперечисленных.
Варианты использования № 3 и № 4 , имеющие
Optional
в поле класса или в структуре данных, считается неправильным использованием API. Во-первых, это идет вразрез с главной целью дизайна,Optional
как указано выше. Во-вторых, это не добавляет никакой ценности.Есть три способа справиться с отсутствием значения в
Optional
: предоставить замещающее значение, вызвать функцию для предоставления замещающего значения или вызвать исключение. Если вы сохраняете в поле, вы должны сделать это во время инициализации или назначения. Если вы добавляете значения в список, как упоминалось в OP, у вас есть дополнительный выбор - просто не добавлять значение, тем самым «выравнивая» отсутствующие значения.Я уверен, что кто-то может придумать несколько надуманных случаев, когда они действительно хотят хранить их
Optional
в поле или коллекции, но в целом лучше избегать этого.источник
Optional
as, когда что-то должно быть сделано или не должно быть сделано при наличии или отсутствии значения.if(foo == null)
это лучше, чем просто хранить поле какoptional
. И я не понимаю, почему явныйgetOptionalFoo()
внутренний вызов лучше, чем просто сохранение поля какOptional
. Кроме того, если одно поле может бытьnull
и одно поле не может бытьnull
, почему бы не сообщить об этом компилятору, создав ихOptional
или нетOptional
.Optional<>
экспертов: «не хранитеOptional<>
в поле». Я действительно пытаюсь понять смысл этой рекомендации. Рассмотрим дерево DOM, где у узла может быть родитель или нет. Кажется, в случае использования,Node.getParent()
который вернетсяOptional<Node>
. Неужели я собираюсь хранитьnull
и переносить результат каждый раз? Зачем? Что может принести мне эта дополнительная неэффективность, кроме того, что мой код выглядит ужасно?Optional<Node> getParent() { return Optional.ofNullable(parent); }
. Это выглядит дорого, потому что он выделяетOptional
каждый раз! Но если вызывающий распаковывает его немедленно, объект является чрезвычайно недолговечным и никогда не продвигается из райского пространства. Это может быть даже устранено анализом побега JIT. Суть ответа в следующем: «это зависит», но использованиеOptional
в полях потенциально увеличивает объем использования памяти и замедляет обход структуры данных. И, наконец, я думаю, что это загромождает код, но это дело вкуса.Я опаздываю к игре, но за то, что она того стоит, я хочу добавить свои 2 цента. Они идут вразрез с целью разработки
Optional
, которая хорошо описана в ответе Стюарта Маркса , но я все еще убежден в их обоснованности (очевидно).Использовать дополнительно везде
В основном
Я написал целую запись в блоге об использовании,
Optional
но в основном это сводится к следующему:Optional
вместоnull
Первые два исключения могут уменьшить воспринимаемые накладные расходы на упаковку и развертывание ссылок в
Optional
. Они выбраны так, что нуль никогда не может законно пройти границу из одного экземпляра в другой.Обратите внимание, что это почти никогда не допустит
Optional
s в коллекциях, что почти так же плохо, какnull
s. Просто не делай этого. ;)По поводу ваших вопросов
преимущества
Это уменьшает присутствие
null
s в вашей кодовой базе, хотя и не уничтожает их. Но это даже не главное. Есть и другие важные преимущества:Проясняет намерение
Использование
Optional
четко выражает, что переменная, ну, необязательно. Любому читателю вашего кода или потребителю вашего API придется биться из-за того, что там ничего не может быть, и что необходима проверка перед доступом к значению.Устраняет неопределенность
Без
Optional
смыслаnull
происшествия неясно. Это может быть юридическое представление состояния (см.Map.get
) Или ошибка реализации, например, отсутствующая или неудачная инициализация.Это резко меняется с постоянным использованием
Optional
. Здесь уже появлениеnull
означает наличие ошибки. (Потому что, если бы значение могло быть пропущено, использовалсяOptional
бы an .) Это значительно облегчает отладку исключения с нулевым указателем, поскольку на вопрос о значении этогоnull
уже дан ответ.Больше нулевых проверок
Теперь, когда ничего не может быть
null
больше , это можно применять везде. При использовании аннотаций, утверждений или простых проверок вам никогда не придется задумываться о том, может ли этот аргумент или тип возвращаемого значения быть нулевым. Это не может!Недостатки
Конечно, нет серебряной пули ...
Производительность
Упаковка значений (особенно примитивов) в дополнительный экземпляр может снизить производительность. В тесных петлях это может стать заметным или даже хуже.
Обратите внимание, что компилятор может обойти дополнительную ссылку для кратковременного времени жизни
Optional
s. В Java 10 типов значений могут дополнительно уменьшить или убрать штраф.Сериализация
Optional
не сериализуем, но обходной путь не слишком сложен.неизменность
Из-за неизменности универсальных типов в Java некоторые операции становятся громоздкими, когда фактический тип значения помещается в аргумент универсального типа. Пример приведен здесь (см. «Параметрический полиморфизм») .
источник
List
s. Это тот случай, когда важно, чтобы определенный элемент находился по определенному индексу, но элемент может присутствовать или нет. Это ясно сообщаетсяList<Optional<T>>
типом. Если, с другой стороны, важно, какие объекты являются членами какой-то коллекции, то в этом нет никакого смысла.Optional
, переданное в метод, может бытьnull
. Так что вы приобрели? Теперь у вас есть две проверки. См. Stackoverflow.com/a/31923042/650176Optional
(что должно произойти, если вы хотите передать это в качестве аргументов),null
это никогда не будет легальной ценностью. Поэтому вызов метода с явным параметромnull
, очевидно, является ошибкой.Лично я предпочитаю использовать инструмент проверки кода IntelliJ, чтобы использовать его
@NotNull
и@Nullable
проверять, так как это в основном время компиляции (может иметь некоторые проверки во время выполнения). Это снижает издержки с точки зрения читабельности кода и производительности во время выполнения. Это не так строго, как использование Optional, однако это отсутствие строгости должно быть подкреплено достойными юнит-тестами.Это работает с Java 5 и не нужно оборачивать и разворачивать значения. (или создайте объекты-оболочки)
источник
@Nonnull
лично - работает с FindBugs;)@Nonnull @NotNull etc
Я думаю, что Гуава Факультативно и их вики-страница довольно хорошо это описывают:
Optional
добавляет некоторые накладные расходы, но я думаю, что его явное преимущество заключается в том, чтобы сделать явным, что объект может отсутствовать, и это заставляет программистов справляться с ситуацией. Это предотвращает то, что кто-то забудет любимого!= null
чек.Взяв пример 2 , я думаю, что это гораздо более явный код для записи:
чем
Для меня
Optional
лучше фиксирует тот факт, что звуковой карты нет.Мои 2 ¢ о ваших баллах:
public Optional<Foo> findFoo(String id);
- Я не уверен в этом. Может быть, я бы вернул,Result<Foo>
который может быть пустым или содержатьFoo
. Это похожая концепция, но не совсемOptional
.public Foo doSomething(String id, Optional<Bar> barOptional);
- Я бы предпочел @Nullable и проверку findbugs, как в ответе Питера Лори - см. Также это обсуждение .Optional<Index> getIndex()
чтобы явно указать, что книга может не иметь индекса.В общем, я бы попытался свести к минимуму прохождение вокруг
null
s. (После того, как сгорел ...) Я думаю, что стоит найти соответствующие абстракции и указать коллегам-программистам, что на самом деле представляет определенное возвращаемое значение.источник
soundcard.ifPresent(System.out::println)
. ВызовisPresent
семантически такой же, как проверкаnull
.if(soundcard != null && soundcard.isPresent())
. Хотя создание API, которое возвращает Optional и может также возвращать NULL, я надеюсь, что никто этого не сделает.Из учебника Oracle :
источник
Вот хорошая статья, которая показывает полезность варианта использования №1. Там этот код
превращается в это
с помощью Факультативно в качестве возвращаемого значения соответствующих геттерных методов.
источник
Вот интересное использование (я считаю) для ... Тесты.
Я намерен серьезно протестировать один из моих проектов и поэтому строю утверждения; только есть вещи, которые я должен проверить, а другие - нет.
Поэтому я создаю вещи для утверждения и использую утверждение для их проверки, например так:
В классе NodeAssert я делаю это:
Что означает, что утверждение действительно только срабатывает, если я хочу проверить метку!
источник
Optional
Класс позволяет вам избегать использованияnull
и предоставляет лучшую альтернативу:Это побуждает разработчика делать проверки на наличие, чтобы избежать неперехвата
NullPointerException
.API становится лучше документированным, потому что можно увидеть, где ожидать значений, которые могут отсутствовать.
Optional
предоставляет удобный API для дальнейшей работы с объектомisPresent()
:;get()
;orElse()
;orElseGet()
;orElseThrow()
;map()
;filter()
;flatmap()
,Кроме того, многие фреймворки активно используют этот тип данных и возвращают его из своего API.
источник
В Java просто не используйте их, если вы не зависимы от функционального программирования.
Им не место в качестве аргументов метода (я обещаю, что однажды кто-нибудь передаст вам необязательное значение null, а не просто необязательное значение, которое будет пустым).
Они имеют смысл для возвращаемых значений, но они предлагают клиентскому классу продолжать растягивать цепочку построения поведения.
FP и цепочки мало места в императивном языке, таком как java, потому что его очень трудно отлаживать, а не просто читать. Когда вы выходите на линию, вы не можете знать ни состояние, ни цель программы; Вы должны войти в это, чтобы понять это (в коде, который часто не ваш и имеет много фреймов стека, несмотря на пошаговые фильтры), и вам нужно добавить множество точек останова, чтобы убедиться, что он может остановиться в добавленном вами коде / лямбде, вместо простого обхода тривиальных строк if / else /.
Если вы хотите функциональное программирование, выберите что-то еще, кроме java, и надеюсь, что у вас есть инструменты для его отладки.
источник
Я не думаю, что Optional является общей заменой методов, которые потенциально могут возвращать нулевые значения.
Основная идея такова: отсутствие значения не означает, что оно потенциально доступно в будущем. Это разница между findById (-1) и findById (67).
Основная информация о дополнительных возможностях для вызывающего абонента заключается в том, что он может не рассчитывать на данное значение, но оно может быть доступно через некоторое время. Может быть, он снова исчезнет и вернется позже еще раз. Это как выключатель. У вас есть «опция», чтобы включить или выключить свет. Но у вас нет выбора, если у вас нет света, чтобы включить.
Поэтому я нахожу это слишком грязным, чтобы вводить Optionals везде, где ранее мог быть возвращен нуль Я все еще буду использовать ноль, но только в ограниченных областях, таких как корень дерева, ленивая инициализация и явные методы поиска.
источник
Кажется ,
Optional
это только полезно , если тип T в Опционно примитивный тип , какint
,long
,char
и т.д. Для «реальных» классов, это не имеет смысла для меня , как вы можете использоватьnull
значение в любом случае.Я думаю, что это было взято отсюда (или из другой подобной языковой концепции).
Nullable<T>
В C # это
Nullable<T>
было введено давно, чтобы обернуть типы значений.источник
Optional
самом деле это грабежjava.util.Optional
.An имеет семантику, аналогичную неизменяемому экземпляру шаблона проектирования Iterator :
Optional
isPresent()
)get()
), если это действительно относится к объектуnext()
метода).Поэтому подумайте о возврате или передаче
Optional
в контекстах, в которых вы, возможно, ранее рассматривали возможность использования JavaIterator
.источник
Java SE 8 представляет новый класс с именем java.util.Optional,
Вы можете создать пустой Optional или Optional с нулевым значением.
А вот Optional с ненулевым значением:
Сделайте что-нибудь, если значение присутствует
Теперь, когда у вас есть дополнительный объект, вы можете получить доступ к доступным методам, чтобы явно иметь дело с наличием или отсутствием значений. Вместо того, чтобы не забыть сделать проверку на ноль, следующим образом:
Вы можете использовать метод ifPresent () следующим образом:
источник