我最近阅读了有关此内容的内容,并看到人们在使用此类,但在几乎所有情况下,使用效果null
都很好-如果不是更直观的话。有人可以提供一个具体的例子,说明在哪些Optional
方面null
做不到或做得更干净的事情吗?我唯一能想到的就是将其与Maps
不接受null
键的键一起使用,但是即使这样,也可以通过对值的空值进行“映射”来实现。谁能为我提供更令人信服的论点?谢谢。
我最近阅读了有关此内容的内容,并看到人们在使用此类,但在几乎所有情况下,使用效果null
都很好-如果不是更直观的话。有人可以提供一个具体的例子,说明在哪些Optional
方面null
做不到或做得更干净的事情吗?我唯一能想到的就是将其与Maps
不接受null
键的键一起使用,但是即使这样,也可以通过对值的空值进行“映射”来实现。谁能为我提供更令人信服的论点?谢谢。
Answers:
番石榴团队成员在这里。
可能最大的缺点null
是,在任何给定的上下文中,它的含义不明显:它没有说明性的名称。并非总是很明显,这null
意味着“此参数没有值” –哎呀,作为返回值,有时意味着“错误”,甚至是“成功”(!!),或者仅仅是“正确的答案是什么”。Optional
通常是使变量可为空时实际上指的概念,但并非总是如此。如果不是这样,我们建议您编写自己的类(类似于Optional
但使用不同的命名方案)来阐明您的实际含义。
但是我要说的最大优点Optional
不是可读性:优点是其防白痴。如果您要完全编译程序,它会迫使您积极考虑不存在的情况,因为您必须主动解开Optional
并解决该情况。Null使得简单地忘记事情变得异常容易,尽管FindBugs有所帮助,但我认为它几乎不能解决这个问题。当您返回可能“不存在”的值时,这一点尤其重要。你(及其他)更可能忘记,other.method(a, b)
可以返回一个null
比你可能会忘记,值a
可能是null
当你实现other.method
。归来Optional
使得调用者无法忘记这种情况,因为他们必须自己打开对象。
由于这些原因,我们建议您将其Optional
用作方法的返回类型,但不一定要在方法参数中使用。
(顺便说一句,这是在这里的讨论中总结出来的。)
Map
回报null
,如果一个关键是未映射,但回忆一下,如果你这样做map.put(key, null)
,那么map.containsKey(key)
将返回true
,但map.get(key)
将返回null
。 Optional
在清除“明确映射为null”情况和“在地图中不存在”情况之间的区别时很方便。我会承认Optional
可以滥用,但我还不确定您所描述的案例是滥用行为。
Optional<T>
是 “已找到但无效”的值。或更准确地说,Optional<T>
是一种使用额外的“已找到但无效的”值修饰任何类型的方法T
-通过组合两个现有类型来创建新类型。如果您有一百个类,则必须为每个类创建一个单独的“已找到但无效”的值,这很麻烦,但Optional<T>
可以轻松地对所有它们使用。
它看起来真的很像Maybe
Haskell 的Monad模式。
您应该阅读以下内容,Wikipedia Monad(函数式编程):
并在Kerflyn的博客上阅读从Optional到Guad的Monad , 其中讨论了用作Monad的Guava的Optional:
编辑:
在Java8中,有一个内置的Optional,它具有Monadic运算符,例如flatMap
。这一直是一个有争议的主题,但最终得以实现。
看到http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html
public Optional<String> tryFindSimilar(String s) //...
Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar = opt.flatMap(this::tryFindSimilar);
该flatMap
运营商是必不可少的,让一元操作,并允许轻松链调用,所有返回可选结果。
想想看,如果您使用map
运算符5次Optional<Optional<Optional<Optional<Optional<String>>>>>
,则最终flatMap
会得到,而使用会给您Optional<String>
从Java8开始,我宁愿不使用功能不那么强大的Guava的Optional。
使用它的一个很好的理由是,它使您的null非常有意义。可以返回一个“名称”作为替代,而不是返回可能意味着很多事情的空值(例如错误,失败或空值等)。看这个例子:
让我们定义一个基本的POJO:
class PersonDetails {
String person;
String comments;
public PersonDetails(String person, String comments) {
this.person = person;
this.comments = comments;
}
public String getPerson() {
return person;
}
public String getComments() {
return comments;
}
}
现在,让我们使用这个简单的POJO:
public Optional<PersonDetails> getPersonDetailstWithOptional () {
PersonDetails details = null; /*details of the person are empty but to the caller this is meaningless,
lets make the return value more meaningful*/
if (details == null) {
//return an absent here, caller can check for absent to signify details are not present
return Optional.absent();
} else {
//else return the details wrapped in a guava 'optional'
return Optional.of(details);
}
}
现在让我们避免使用null并使用Optional进行检查,使其有意义
public void checkUsingOptional () {
Optional<PersonDetails> details = getPersonDetailstWithOptional();
/*below condition checks if persons details are present (notice we dont check if person details are null,
we use something more meaningful. Guava optional forces this with the implementation)*/
if (details.isPresent()) {
PersonDetails details = details.get();
// proceed with further processing
logger.info(details);
} else {
// do nothing
logger.info("object was null");
}
assertFalse(details.isPresent());
}
因此,最终它是一种使null有意义并减少歧义的方法。
Optional的最重要的优点是它为函数的实现者和调用者之间的协定添加了更多细节。因此,对于参数和返回类型均有用。
如果使约定始终包含Optional
可能的空对象,则可以为以下情况添加更多说明:
Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)
该合同在这里明确规定,有一个机会,结果没有返回,但也表明它会与工作from
和to
为不存在。
Optional<Integer> maxPrime(Optional<Integer> from, Integer to)
合同规定from是可选的,因此缺少的值可能具有特殊的含义,例如start from2。我可以预期to
参数的null值将引发异常。
因此,使用Optional的好处在于,合同既具有描述性(与@NotNull
注解类似)又具有正式性,因为您必须编写代码.get()
来应对Optional
。