Guava 的 Optional 类有啥意义

Posted

技术标签:

【中文标题】Guava 的 Optional 类有啥意义【英文标题】:What's the point of Guava's Optional classGuava 的 Optional 类有什么意义 【发布时间】:2012-03-22 14:36:52 【问题描述】:

我最近读到了这篇文章并看到有人在使用这个类,但在几乎所有情况下,使用null 也可以工作——如果不是更直观的话。有人可以提供一个具体的例子,Optional 将实现null 无法或以更清洁的方式实现的目标吗?我唯一能想到的是将它与不接受null 键的Maps 一起使用,但即使这样也可以通过null 值的侧面“映射”来完成。谁能给我一个更有说服力的论据?

【问题讨论】:

随机咆哮:我讨厌人们过度使用“模式”并让代码看起来如此丑陋,以获得一些不存在的理论上的好处...... 从 Java8 开始,我将不再使用这个 Guava 类,因为它不如 JDK 强大。见***.com/a/10756992/82609 【参考方案1】:

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

如果您约定始终使用 Optional 表示可能的 null 对象,您可以为以下情况添加更多说明:

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

    这里的合同明确规定有可能不返回结果,但也表明它将在fromto 不存在的情况下工作。

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

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

因此,使用 Optional 的好处是合约变得既具有描述性(类似于 @NotNull 注释)又具有正式性,因为您必须编写代码 .get() 来处理 Optional

【讨论】:

为什么要使用 Optional 而空的 List 会更干净? 我在返回时选择了错误的示例。使用集合,您可以使约定始终返回一个空集合而不是一个空值。如果使用此约定,则集合不需要可选。 Optional 可以被视为具有零个或一个元素的集合,因此不需要将其放在其他集合周围。 根据上下文,包含 0 项的列表和不存在的列表之间可能存在显着差异。【参考方案2】:

Guava 团队成员在这里。

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

但我想说Optional 的最大优势不在于可读性:优势在于它的白痴性。如果您希望程序完全编译,它会迫使您积极考虑缺席的情况,因为您必须主动解开Optional 并解决这种情况。 Null 使简单地忘记事情变得非常容易,虽然 FindBugs 有帮助,但我认为它几乎不能解决这个问题。当您返回可能存在或不存在的值时,这一点尤其重要。您(和其他人)更有可能忘记other.method(a, b) 可以返回null 值,而不是在您实现other.method 时忘记a 可能是null。返回Optional 使调用者不可能忘记这种情况,因为他们必须自己解开对象。

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

(顺便说一句,这完全是从讨论here中抄袭的。)

【讨论】:

+1 以获得很好的解释。我不知道这是否是灵感,但我会在 SML、OCaml 和 F# 中添加一个指向选项类型的指针,它们具有许多相同的语义。 从直接参与其中的人那里得到答案总是很棒。我会为此 +1。会+1以获得一个很好的答案。 (但我不能。)我认为将它作为返回值来迫使不熟悉方法的消费者努力承认可以返回“无”是一个令人信服的理由。我不喜欢它是如何被过度使用(甚至被滥用)的。例如,当看到人们将 Optionals 作为值放入 M​​aps 时,我感到非常不安。为不存在/未映射的键映射返回 null 是一个成熟的范例......无需让它更复杂...... 如果键未映射,Map 会返回null,这是公认的,但请记住,如果您执行map.put(key, null),则map.containsKey(key) 将返回true,但map.get(key) 将返回nullOptional 可以方便地清除“显式映射到空”情况和“地图中不存在”情况之间的区别。我承认Optional 可以被滥用,但我还不相信你描述的情况是滥用。 用一个过时的类比,曾经有这些巨大的东西叫做“电话簿”:-) 如果我让你查找某人的号码,你说“那个人没有号码”我会说“你是什么意思,他们在那里有一个未列出的号码,或者你根本找不到他们的条目?”这两个可能的响应分别很好地映射到 Optional.absent() 和 null。 Optional.absent() 是明确的“肯定否定”。 @RAY:在某种程度上,Optional<T> “找到但无效”的值。或者更准确地说,Optional<T> 是decorate 任何类型T 的一种方法,带有一个额外的“找到但无效”的值——通过组合两个现有类型来创建一个新类型。如果您有一百个类,则必须为每个类创建一个单独的“找到但无效”值可能会很麻烦,但Optional<T> 可以轻松地为所有类工作。【参考方案3】:

使用它的一个很好的理由是它使你的空值非常有意义。除了返回一个可能意味着很多事情(如错误、失败或空等)的 null 之外,您还可以在 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());

因此,最终它是一种使空值有意义且减少歧义的方法。

【讨论】:

【参考方案4】:

它看起来真的很像 Haskell 中的 Maybe Monad 模式。

您应该阅读以下内容,***Monad (functional programming):

并阅读Kerflyn's Blog, 上的From Optional to Monad with Guava,其中讨论了用作Monad 的Guava 的Optional:


编辑: 在 Java8 中,有一个内置的 Optional,它具有像 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&lt;Optional&lt;Optional&lt;Optional&lt;Optional&lt;String&gt;&gt;&gt;&gt;&gt;,而使用 flatMap 会得到 Optional&lt;String&gt;

由于Java8我宁愿不使用Guava的Optional,它功能不那么强大。

【讨论】:

以上是关于Guava 的 Optional 类有啥意义的主要内容,如果未能解决你的问题,请参考以下文章

拥有复杂的实体类有啥意义(在 Hibernate 意义上)

WPF 中的 StatusBarItem 类有啥意义?

当你可以做一个接口时,抽象类有啥意义[重复]

Java8 Optional类

类型和类有啥区别?

Guava学习笔记:Optional优雅的使用null