Java 8 中 Optional 类型的有效使用

Valid usage of Optional type in Java 8

这是 Java 8 中 Optional 类型的有效(预期)用法吗?

String process(String s) {
    return Optional.ofNullable(s).orElseGet(this::getDefault);
}

它有点做作,但是 'valid'(如 'syntactically'),但正如@yshavit 指出的那样,它旨在用于库开发。


之前的回答是因为 FP 代码难以阅读。下面有注释(有点冗长,b/c 那是 javadoc 注释)但仍然如此。更容易阅读恕我直言。 (第二个是无评论,至少对齐以帮助提高可读性)

private boolean isFooValid(final Integer id) {
    return getFoo(id)
        // filter if 'f' matches the predicate, return Optional w/f if true, empty Optional if false
        .filter(f -> "Bar".equals(f.getName()) && "US".equals(f.getCountryCode())) 
        // If a value is present, apply the provided mapping function to it, 
        // If non-null, return an Optional describing the result.  
        .map(f -> true)
        // Return the value if present, otherwise return other.
        .orElse(false);
 }

或者至少将它排成一行,这样更容易理解正在发生的事情。

private boolean isFooValid(final Integer id) {
    return getFoo(id)
        .filter(f -> "Bar".equals(f.getName()) && "US".equals(f.getCountryCode())) 
        .map(f -> true)
        .orElse(false);
 }

询问它是否 "valid" 是基于意见的,但至于它是否是 预期的 用例:不,不是。

Oracle 的 Java 语言架构师 Brian Goetz 表示,Optional 的用例适用于需要 "no value" 标记 and when using null for this is likely to cause errors 的情况。具体来说,如果您的方法的合理用户不太可能考虑其结果为 null 的可能性,那么您应该使用 Optional。它明确地 不是 打算成为一般 "Maybe" 类型的对象,因为您在这里使用它。

在您的例子中,returnOptional 的方法是私有的。这意味着它只能由 class 的实现者使用,并且您可以假设他们对 class 方法有很好的了解——包括其中哪些可能 return null.由于没有合理的混淆风险,Brian Goetz 会(可能)说他不会认为这是一个有效的用例。

由于这或多或少是一个基于意见的问题,所以我会把我的问题放进去。如果你想说

if (id == 1) {
    Foo f = new Foo(id, "Bar", "US");
    return "Bar".equals(f.getName()) && "US".equals(f.getCountryCode());
} else {
    return false;
}

那就这么说吧。让事情变得 "functional" 不会自动让事情变得更清晰或更好。通过引入不必要的 Optional、几个 lambda 表达式和一些我不得不查找的 Optional 方法,您使代码更加复杂和难以理解。我认为 Java "intended" 的设计者不会让人们使用 Optional 来帮助使代码更晦涩。

编辑: 阅读了一些回复后,我认为值得添加一些评论。这不是我熟悉的函数式编程习语,这会使它更难理解。我 am 熟悉的习语主要涉及 Java 流,或(在其他语言中)应用于数组或列表或其他多值集合中的多个值的功能习语。在那些情况下,一旦你克服了不熟悉,函数式语法可以被视为一种改进,因为它允许隐藏一些细节(循环索引、迭代器、运行 指针、累加器变量)。所以总的来说,它可以简化事情。这个例子本身并没有做任何这样的简化。

但是,某些 Optional 功能在流上下文中很有用。假设我们有一个 returns 和 Optional<Integer>parseInt() 方法,如果输入字符串无效,则该方法为空。 (Java 8 真的应该提供这个。)这样可以很容易地获取一个字符串数组并生成一个整数数组,其中不解析的字符串被简单地从结果中删除——使用 parseInt 在流 map() 中,并使用流过滤器过滤掉空的 Optional。 (我已经看到多个 Whosebug 问题询问如何执行此操作。)如果您只想保留正值,则可以在使用流过滤器之前使用 Optional.filter() 将非正值更改为 Optional.empty() (虽然在这种情况下,您可以在之后添加另一个流过滤器,但在更复杂的情况下,Optional 过滤器可能更有用)。从功能的角度来看,这就是我认为 Optional 的主要好处。它允许您同时处理一组值,方法是为您提供一种表示 "non-values" 的方法并编写一个仍可与它们一起使用的函数。所以我猜 Optional 的主要用途,除了替代 null 之外,是在将函数应用于整个序列时表示值序列中的空格。

我再试一次。

这是一个有效的用法吗?是的,从狭义上讲,它编译并产生您期望的结果。

这是预期用途吗?不,现在,有时事情会发现它们的用处超出了它们最初的用途,如果成功了,那就太好了。但是对于 Optional,我们发现通常事情不会很顺利。

Brian Goetz 和我在 Java2015 年的一次谈话中与 Optional 讨论了一些问题,API Design With Java 8 Lambda 和流

Optional 的主要用途如下:(幻灯片 36)

Optional is intended to provide a limited mechanism for library method return types where there is a clear need to represent "no result," and where using null for that is overwhelmingly likely to cause errors.

Optional 链接方法的能力无疑非常酷,在某些情况下,它减少了条件逻辑的混乱。但很多时候这是行不通的。典型的代码味道是,代码不是使用方法链接到 handle 和从某个方法返回的 Optional,而是 创建 一个 Optional 来自可为 null 的内容,以便链接方法并避免条件语句。这是一个实际的例子(也来自我们的演示文稿,幻灯片 42):

// BAD
String process(String s) {
    return Optional.ofNullable(s).orElseGet(this::getDefault);
}

// GOOD
String process(String s) {
    return (s != null) ? s : getDefault();
}

使用Optional的方法较长,大多数人觉得它比常规代码更晦涩。不仅如此,它还会无缘无故地产生额外的垃圾。

底线:仅仅因为你可以做某事并不意味着你应该去做。