我有一个返回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
到每种其他类型都不能有特殊的映射方法”,但是Optional
和Stream
有特殊的关系。实际上,我认为Optional
几乎是一个包含零个或一个元素的Stream
。实际上,这正是Optional.stream()
的回报。因此,这实际上是对Optional.stream()
的补充。
答案 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返回可选值的通用机制:由于它们认为(我会完全正确地假定),因此不应该使用可选。