是否存在从Optional到Stream的便利映射方法?

时间:2019-03-30 15:28:39

标签: java java-stream optional

我有一个返回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 <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到每种其他类型都不能有特殊的映射方法”,但是OptionalStream有特殊的关系。实际上,我认为Optional几乎是一个包含零个或一个元素的Stream。实际上,这正是Optional.stream()的回报。因此,这实际上是对Optional.stream()的补充。

1 个答案:

答案 0 :(得分:0)

关于这个问题:“为什么不'他们'将此方法添加到可选方法?”,首先是一个附带的故事。

“角色”是什么意思?

您会在字典中找到许多定义,但绝对没有一个词源上正确的定义,即“遮罩”。您会发现,角色来自“每个儿子”:您所说的话。面具。

可以说,“角色”一词的“作者”认为这意味着“面具”。但是语言是动态的事物,其真实含义可能更多地取决于单词的使用方式,而不取决于作者的含义。

返回到可选。

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

社区中的某些人不太习惯这种方式。有人更像scala的可选方法那样对待它:作为将无效信息携带到类型系统中的“解决方案”。

这是极不明智的建议。作为动态概念的null可能存在问题,并且存在更好的解决方案(有争议的解决方案,但尚有待商,,但超出了此问题的范围,没有重点),但在绝对没有研究或思想的情况下,对Optional直言不讳地回答了:显然不好。我们已经看到了这一点:Java已有20年成为全球最受欢迎的语言之一的历史,并且已经建立了一个巨大的图书馆生态系统。您不能以向后兼容的方式将返回X的API替换为返回Optional<X>,这意味着这20年的库中的每一个最后一个都需要像昨天的垃圾一样被取出,因为无法转换API。

通常,任何一种更新语言的提议都将需要可笑地不足。但是,如果整个社区采用这样的观念,那就恰恰是需要的:应该通过API以返回Optional<T>的形式来传递可选的返回值。

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

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

现在呢?关于如何在Java中修复“空”,我有各种想法,尽管首先您应该讨论它是否甚至是需要修复的问题。远远超出了这个问题的范围。

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