Java 8中Optional类型的有效用法
这是Java 8中Optional类型的有效(预期)用法吗?
class Foo { private final Integer id; private final String name; private final String countryCode; Foo(final Integer id, final String name, final String countryCode) { this.id = id; this.name = name; this. countryCode = countryCode; } public String getName() { return name; } public String getCountryCode() { return countryCode; } } class FooSrv { private Optional getFoo(final Integer id) { return id != null && id == 1 ? Optional.of(new Foo(id, "Bar", "US")) : Optional.empty(); } private boolean isFooValid(final Integer id) { return getFoo(id) .filter(f -> "Bar".equals(f.getName()) && "US".equals(f.getCountryCode())) .map(f -> true) .orElse(false); } }
我会再接下来。
这是有效的用法吗? 是的,从狭义上讲它会编译并产生您期望的结果。
这是用途吗? 不,现在,有时事情发现它们的用处超出了原来的范围,如果这样做有效,那就太好了。 但是对于Optional
,我们发现通常情况不会很好。
Brian Goetz和我讨论了JavaOne 2015演讲中的Optional
一些问题, 使用Java 8 Lambdas和Streams进行API设计 :
- 链接到video
- 链接到幻灯片
Optional
的主要用途如下:(幻灯片36)
可选的目的是为库方法返回类型提供有限的机制,其中明确需要表示“无结果”,并且使用
null
的情况极有可能导致错误 。
从Optional
链接方法的能力无疑是非常酷的,并且在某些情况下它减少了条件逻辑的混乱。 但通常情况下这并没有成功。 一个典型的代码气味是,代替使用方法链接来处理从某个方法返回的Optional
的代码,它从可以为空的东西创建一个Optional
,以便链接方法并避免条件。 这是一个实际的例子(也来自我们的演示文稿,幻灯片42):
// BAD String process(String s) { return Optional.ofNullable(s).orElseGet(this::getDefault); } // GOOD String process(String s) { return (s != null) ? s : getDefault(); }
使用Optional
的方法更长,大多数人发现它比传统代码更模糊。 不仅如此,它还没有充分的理由造成额外的垃圾。
底线:仅仅因为你可以做某事并不意味着你应该这样做。
由于这或多或少是一个基于意见的问题,我会抛弃我的。如果你想说的话
if (id == 1) { Foo f = new Foo(id, "Bar", "US"); return "Bar".equals(f.getName()) && "US".equals(f.getCountryCode()); } else { return false; }
然后就这么说吧。 使事物“有效”不会自动使事情更清晰或更好。 通过引入一个不必要的Optional
,一对lambdas,以及一些我必须查找的Optional
方法,您已经使代码更复杂且难以理解。 我不认为Java的设计者“有意”让人们使用Optional
来帮助使代码更加模糊。
编辑:在阅读了一些回复后,我认为值得添加一些评论。 这不是我熟悉的函数式编程习惯,这将使其更难理解。 我熟悉的习语主要涉及Java流,或(在其他语言中)应用于数组或列表中的多个值或多个值的其他集合的function习语。 在这些情况下,一旦你超越了陌生程度,function语法就可以看作是一种改进,因为它允许隐藏一些细节(循环索引,迭代器,运行指针,累加器变量)。 总的来说,它可以简化事情。 这个例子本身并没有做任何这样的简化。
但是,某些Optional
function在流上下文中很有用。 假设我们有一个parseInt()
方法,它返回一个Optional
,如果输入字符串无效,则为空。 (Java 8确实应该提供这个。)这样可以很容易地获取一个字符串数组并生成一个整数数组,其中不解析的字符串只是从结果中删除 – 在流map()
使用parseInt
map()
,并使用流filter过滤掉空的Optional
s。 (我已经看到多个StackOverflow问题,询问如何执行此操作。)如果您只想保留正值,可以使用Optional.empty()
在使用流filter之前将nonposit更改为Optional.empty()
(虽然在这种情况下,您可以在之后添加另一个流filter,但在更复杂的情况下, Optional
filter可能更有用)。 从function的角度来看,这就是我认为Optional
的主要优点。 它允许您一次处理一组值,方法是为您提供一种表示“非值”的方法,并编写一个仍然可以使用它们的函数。 所以我想除了替换null
之外, Optional
的主要用途是在整个序列中应用函数时表示值序列中的空格。
询问它是否“有效”是基于意见的,但是它是否是预期的用例:不,它不是。
Oracle的Java语言架构师Brian Goetz表示,Optional的用例适用于需要“无值”标记的情况, 并且当使用null
,可能会导致错误 。 具体来说,如果您的方法的合理用户不太可能考虑其结果为null
的可能性,那么您应该使用Optional。 它显然不是一般的“可能”类型的对象,因为你在这里使用它。
在您的情况下,返回Optional的方法是private。 这意味着它只能由类的实现者使用,并且您可以假设他们对类的方法有很好的了解 – 包括哪些方法可能返回null
。 由于没有合理的混淆风险,Brian Goetz(可能)会说他不会认为这是一个有效的用例。
它有点人为,但“有效”(如’语法’),但正如@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); }