番石榴的选修课有什么意义


89

我最近阅读了有关此内容的内容,并看到人们在使用此类,但在几乎所有情况下,使用效果null都很好-如果不是更直观的话。有人可以提供一个具体的例子,说明在哪些Optional方面null做不到或做得更干净的事情吗?我唯一能想到的就是将其与Maps不接受null键的键一起使用,但是即使这样,也可以通过对值的空值进行“映射”来实现。谁能为我提供更令人信服的论点?谢谢。


12
随机语:当人们过度使用“模式”并使代码看起来如此丑陋以达到某些不存在的理论上的好处时,我讨厌它……
RAY 2012年

从Java8开始,我不再使用此Guava类,因为它不如JDK强大。参见stackoverflow.com/a/10756992/82609
塞巴斯蒂安·洛伯

Answers:


155

番石榴团队成员在这里。

可能最大的缺点null是,在任何给定的上下文中,它的含义不明显:它没有说明性的名称。并非总是很明显,这null意味着“此参数没有值” –哎呀,作为返回值,有时意味着“错误”,甚至是“成功”(!!),或者仅仅是“正确的答案是什么”。Optional通常是使变量可为空时实际上指的概念,但并非总是如此。如果不是这样,我们建议您编写自己的类(类似于Optional但使用不同的命名方案)来阐明您的实际含义。

但是我要说的最大优点Optional不是可读性:优点是其防白痴。如果您要完全编译程序,它会迫使您积极考虑不存在的情况,因为您必须主动解开Optional并解决该情况。Null使得简单地忘记事情变得异常容易,尽管FindBugs有所帮助,但我认为它几乎不能解决这个问题。当您返回可能“不存在”的值时,这一点尤其重要。你(及其他)更可能忘记,other.method(a, b)可以返回一个null比你可能会忘记,值a可能是null当你实现other.method。归来Optional 使得调用者无法忘记这种情况,因为他们必须自己打开对象。

由于这些原因,我们建议您将其Optional用作方法的返回类型,但不一定要在方法参数中使用。

(顺便说一句,这是在这里的讨论中总结出来的。)


3
+1是一个很好的解释。我不知道这是否是我的灵感,但是我将添加一个指向SML,OCaml和F#中的选项类型的指针,它们具有许多相同的语义。
亚当·米哈尔辛

2
从直接参与的人那里得到答案总是很高兴。我只为此+1。最好再加+1。(但是我不能。)我认为将其作为返回值以迫使不熟悉方法的消费者努力承认“没有任何东西可以返回”的观点是一个令人信服的原因。我不喜欢它被过度使用(甚至滥用)的方式。例如,当我看到人们将Optionals作为值放入Maps时,我感到非常不安。为不存在/未映射的密钥重设null的映射是一种公认​​的范例...无需使其更加复杂...
RAY

9
这是很确定的是Map回报null,如果一个关键是未映射,但回忆一下,如果你这样做map.put(key, null),那么map.containsKey(key)将返回true,但map.get(key)将返回nullOptional在清除“明确映射为null”情况和“在地图中不存在”情况之间的区别时很方便。我会承认Optional可以滥用,但我还不确定您所描述的案例是滥用行为。
Louis Wasserman'3

8
用过时的比喻,曾经有这些巨大的东西叫做“电话簿” :-),如果我要你查询某人的电话号码,而你说“那个人没有电话号码”,我会说“你做什么?是说,他们在那里有一个未列出的号码,或者您根本找不到他们的条目?” 这两个可能的响应分别很好地映射到Optional.absent()和null。Optional.absent()是确定的“正负”。
凯文·布劳里恩

3
@RAY:从某种意义上说,Optional<T> “已找到但无效”的值。或更准确地说,Optional<T>是一种使用额外的“已找到但无效的”值修饰任何类型的方法T-通过组合两个现有类型来创建新类型。如果您有一百个类,则必须为每个类创建一个单独的“已找到但无效”的值,这很麻烦,但Optional<T>可以轻松地对所有它们使用。
Daniel Pryden 2012年

9

它看起来真的很像MaybeHaskell 的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。


6

使用它的一个很好的理由是,它使您的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有意义并减少歧义的方法。


4

Optional的最重要的优点是它为函数的实现者和调用者之间的协定添加了更多细节。因此,对于参数和返回类型均有用。

如果使约定始终包含Optional可能的空对象,则可以为以下情况添加更多说明:

  1. Optional<Integer> maxPrime(Optional<Integer> from, Optional<Integer> to)

    该合同在这里明确规定,有一个机会,结果没有返回,但也表明它会与工作fromto为不存在。

  2. Optional<Integer> maxPrime(Optional<Integer> from, Integer to)

    合同规定from是可选的,因此缺少的值可能具有特殊的含义,例如start from2。我可以预期to参数的null值将引发异常。

因此,使用Optional的好处在于,合同既具有描述性(与@NotNull注解类似)又具有正式性,因为您必须编写代码.get()来应对Optional


当空列表更干净时,为什么要使用Optional <List>?
2013年

我选择了错误的回报示例。使用集合,您可以使约定始终返回一个空集合,而不是一个空值。如果使用此约定,则集合不需要可选的。可选可以被视为具有零个或一个元素的集合,因此不需要将其放在其他集合周围。
risercostin

2
取决于上下文,包含0个项目的列表和不存在的列表之间可能会有有意义的区别。
plasma147
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.