【问题标题】:Is there a convenience mapping method from Optional to Stream?是否有从 Optional 到 Stream 的便捷映射方法?
【发布时间】:2019-03-30 15:28:38
【问题描述】:

我有一个返回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())。)

对于那些需要具体用例的人,假设有一种方法可以从 DOM 树中返回 HTML <head> 元素:

/**
 * 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 &lt;meta&gt; 元素(如果它们存在)。所以它想搜索&lt;head&gt; 元素的子元素,但当然前提是它存在。 (请注意,childElementsByNameNS() 是一种实用方法,可将另一个元素的子元素检索为 Stream&lt;Element&gt;。)

/**
 * 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 到其他所有类型的特殊映射方法”,但是OptionalStream 有一种特殊的关系。事实上,我有点认为Optional 几乎是一个带有零个或一个元素的Stream。事实上,这正是Optional.stream() 所回馈的。所以这真的是对Optional.stream()的补充。

【问题讨论】:

  • 那不也取决于bars(foo)的签名吗?问题的相关细节很重要。例如它将如何处理这种情况Stream.of(optionalFoo) .flatMap(a -&gt; bars(a.orElse(null)))。无论哪种方式,我真的认为不需要这样的streamMap 来自可选。可能只是基于意见,但当前示例中的用例也不是很清楚。
  • "这不也取决于bars(foo)的签名吗?"怎么会这样?当然,参数并不重要——这就是为什么整个东西都被包裹在一个函数 lambda 中。而且返回的流类型无关紧要——这就是为什么我们有带有捕获和通配符的泛型。所以我真的不明白你在说什么。并且说“我认为没有必要”而不说为什么也没有真正澄清太多。您从示例中看到,这将消除对样板文件.orElse(Stream.empty()) 的需要,对吧?那么您是说您认为不需要删除此样板文件吗?
  • @Naman 我添加了具体示例,因此您可以看到这不依赖于bars(foo) 的签名。
  • 您的前两个代码示例对我来说似乎完全合理。考虑到你的用例不是很常见,并且考虑到它对可读性没有太大的损害,我不确定我会称之为“样板”。

标签: java java-stream optional


【解决方案1】:

至于问题:“为什么'他们'不将此方法添加到可选?”,首先是一个插曲。

“角色”是什么意思?

您会在字典中找到许多定义,但绝对没有一个是词源正确的定义,即“面具”。你看,角色来自“个人声纳”:你说的东西。一个面具。

可以说,persona 这个词的“作者”认为它的意思是“面具”。但是语言是动态的东西,其真正含义可能更多地取决于单词的使用方式,而不是作者的意思。

返回可选。

optional 的作者(Brian Goetz 和 Oracle 的 Lambda 项目的其余部分)将其编写为流操作终端的解决方案,这对于空流可能没有意义。如果列表为空,list.stream().max(someComparator) 的返回值是多少?而已。这就是j.u.Optional全部

社区中的一些人并不完全以这种方式使用它。有些人认为它更像是 scala 的可选:作为将无效信息带入类型系统的“解决方案”。

这是非常不明智的。也许 null 作为一个动态概念存在问题并且存在更好的解决方案(值得商榷,但超出了这个问题的范围,而不是重点),但是鉴于绝对没有研究或思考,下意识地认为 Optional 是那个答案,是显然不好。我们已经可以看到这一点:Java 有 20 年成为地球上最受欢迎的语言之一的历史,并且正如人们所期望的那样,它建立了一个庞大的库生态系统。您不能以向后兼容的方式将返回 X 的 API 替换为返回 Optional&lt;X&gt;,这意味着这 20 年来库中的几乎每一个库都需要像昨天的垃圾一样被取出,因为没有办法转换 API。

通常,任何更新语言的提议都需要几乎所有现有的库来发布重大更新,这将是可笑的不足。然而,如果整个社区都采用这样的概念,即应该通过 API 以返回 Optional&lt;T&gt; 的形式传达可选返回值,那么这正是所需要的。

换句话说,“他们”(主要是 Oracle,以及 JCP 的其余部分)绝对不认为应该使用 Optional。

然而,社区确实使用它。甲骨文仍然认为它的意思是“面具”,一半的社区坚持它绝对是“面具”的意思,但另一半社区开始使用“角色”来表示剧中角色的概念。

现在呢?我对如何在java中修复'null'有各种想法,但首先你应该讨论它是否甚至是一个需要修复的问题。远远超出了这个问题的范围。

但是,希望这篇关于 java 的 null 以及 Optional 如何不能替代它的信件能够阐明为什么 Oracle 或 JCP 中的任何其他人不“只是”将一些方法堆积到 j.u.Optional 中以使其更适合将其用作从 API 返回可选值的通用机制:因为他们认为(我会完全正确地假设),不应该为此使用可选的

【讨论】:

  • Brian Goetz seems clear Optional 的目的是返回一个可能不可用的结果。这正是我使用它的方式。如果值 is 可用,我想调用一个返回流的方法。我不想要.orElse(Stream.empty()) 的额外样板。你写了一段又一段关于你为什么不喜欢Optional的段落,这真的是离题了。
  • 我在问题中添加了具体示例。在我看来,这似乎完全符合 Brian Goetz 的 stated purposeOptional
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-20
  • 1970-01-01
  • 1970-01-01
  • 2011-08-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多