Есть ли удобный способ сопоставления от Необязательного к Потоку? - PullRequest
2 голосов
/ 30 марта 2019

У меня есть метод, который возвращает Optional<Foo>, и если он присутствует, я хочу использовать значение для поиска Stream<Bar>.

Теперь я знаю, что могу сделать это:

optionalFoo.map(foo->bars(foo)).orElse(Stream.empty());

(я использую полную форму функции отображения вместо ссылки на метод для иллюстрации.)

А с Java 9+ я могу сделать это:

optionalFoo.stream().flatMap(foo->bars(foo));

Но в любом случае дополнительный шаг кажется бессмысленным. Существует ли метод отображения, который просто отобразит необязательный поток, или пустой поток, если необязательный отсутствует? Тогда я мог бы сделать:

optionalFoo.streamMap(foo->bars(foo));

Если такого метода нет, то почему бы и нет? Его можно добавить как простой метод по умолчанию для Optional.

И, наконец, если мне нужно написать свой собственный служебный метод, какой из вышеперечисленных подходов более эффективен (учитывая, что они оба дают одинаковый результат): отображение в поток или пустой поток; или преобразование в поток, а затем плоское отображение в поток? Я бы предположил, что первый более эффективен, что иллюстрирует, почему нужен служебный метод. (Мы не хотим перепечатывать .orElse(Stream.empty()) повсеместно.)

Для тех из вас, кому нужен конкретный вариант использования, предположим, что существует метод для возврата элемента HTML <head> из дерева DOM:

/**
 * Finds the {@code <html><head>} element in the HTML namespace.
 * @param document The document tree.
 * @return A reference to the {@code <html><head>} element if it exists in the tree.
 */
public static Optional<Element> findHtmlHeadElement(@Nonnull final Document document);

Теперь другой метод хочет вернуть все элементы HTML <meta>, если они существуют. Поэтому он хочет найти дочерние элементы элемента <head>, но, конечно, только если он существует. (Обратите внимание, что childElementsByNameNS() - это служебный метод, который извлекает дочерние элементы другого элемента как Stream<Element>.)

/**
 * Finds the {@code <html><head><meta>} elements in an HTML document.
 * @param document The document tree.
 * @return A stream of {@code <html><head><meta>} elements if they exist in the tree.
 */
public static Stream<Element> htmlHeadMetaElements(@Nonnull final Document document) {
  return findHtmlHeadElement(document).map(headElement ->
      childElementsByNameNS(headElement, XHTML_NAMESPACE_URI_STRING, ELEMENT_META))
          .orElse(Stream.empty());
}

Зачем мне нужен ужасный шаблон .orElse(Stream.empty())? Я бы предпочел что-то вроде этого:

/**
 * Finds the {@code <html><head><meta>} elements in an HTML document.
 * @param document The document tree.
 * @return A stream of {@code <html><head><meta>} elements if they exist in the tree.
 */
public static Stream<Element> htmlHeadMetaElements(@Nonnull final Document document) {
  return findHtmlHeadElement(document).streamMap(headElement ->
      childElementsByNameNS(headElement, XHTML_NAMESPACE_URI_STRING, ELEMENT_META));
}

Вы можете сказать, что «у вас не может быть специальных методов отображения от Optional для любого другого типа», но Optional и Stream имеют особые отношения. На самом деле я думаю, что Optional почти как Stream с нулем или одним элементом. На самом деле это именно то, что Optional.stream() возвращает. Так что это действительно дополняет Optional.stream().

1 Ответ

0 голосов
/ 30 марта 2019

Что касается вопроса: «Почему« они »не добавляют этот метод к необязательному?», Сначала второстепенная история.

Что означает «персона»?

ВыВы найдете много определений в словаре, и абсолютно ни одно из них не является этимологически правильным, то есть «маской».Видите ли, персона происходит от «per sonare»: то, через что вы говорите.Маска.

«Автор», так сказать, слова «персона» думает, что оно означает «маска».Но языки - это динамические вещи, истинное значение которых, возможно, зависит больше от того, как используется слово, и меньше от значения автора.

Назад к необязательному.

Автор необязательного (Брайан Гетц и остальныеProject Lambda в Oracle) написал его как решение для терминалов потоковой работы, которое может не иметь смысла для пустых потоков.Какое значение будет возвращено list.stream().max(someComparator), если список пуст?Вот и все.Это все , для которых когда-либо предназначался j.u.Optional.

Некоторые в сообществе не совсем так его используют.Некоторые воспринимают это как, скажем, необязательное scala: как «решение» для переноса информации об аннулировании в систему типов.

Это крайне не рекомендуется.Может быть пустыми как динамические концепций есть проблемы и лучшее решение существует (спорно, но выходит за рамки этого вопроса, а не точка), но колено подергивание на Опционна в том, что ответ, в свете совершенно нет исследований или мысли, являетсяочевидно плохо.Мы уже можем видеть это: Java уже 20 лет является одним из самых популярных языков на планете и создала гигантскую экосистему библиотек, как и следовало ожидать.Вы не можете заменить API, который возвращает X на возвращение Optional<X> обратно совместимым способом, что означает, что почти каждый последний из этих 20 лет библиотек должен быть удален как вчерашний мусор, потому что нет никакого способапереходите API.

Обычно любое предложение об обновлении языка, которое потребовало бы почти каждую существующую библиотеку, когда-либо написанную для выпуска срочного обновления, было бы смехотворно неадекватным .И все же это именно то, что необходимо, если сообщество в целом примет идею о том, что необязательные возвращаемые значения должны передаваться через API в форме возврата Optional<T>.

Другими словами, «они»(Oracle, в основном, вместе с остальной частью JCP) определенно не считают, что именно для этого следует использовать Optional.

И, тем не менее, сообщество его использует.Oracle по-прежнему считает, что это означает «маска», половина сообщества настаивает на том, что это определенно означает «маска», но другая половина сообщества начинает использовать «персону» для обозначения понятия персонажа в пьесе.

Что теперь?У меня есть различные идеи о том, как исправить «ноль» в Java, хотя сначала вы должны обсудить, является ли это проблемой, требующей решения.Это далеко выходит за рамки этого вопроса.

Однако, надеюсь, это сообщение о нулевом java и о том, что Optional не является заменой для него, проливает некоторый свет на то, почему Oracle или кто-либо еще в JCP не «просто» складываютнекоторые методы в j.u.Optional, чтобы сделать его более подходящим для использования его в качестве общего механизма для возврата необязательных значений из API: поскольку они думают (и я бы сказал, совершенно правильно), что не следует использовать для этого .

...