在 Scala 中什么时候应该使用 Option.empty[A] 什么时候应该使用 None?

When should I use Option.empty[A] and when should I use None in Scala?

在 Scala 中,当我想将某些内容设置为 None 时,我有几个选择:使用 NoneOption.empty[A]

我应该只选择一个并持续使用它,还是有时我应该使用一个而不是另一个?

示例:

scala> def f(str: Option[String]) = str
f: (str: Option[String])Option[String]

scala> f(None)
res0: Option[String] = None

scala> f(Option.empty)
res1: Option[String] = None

以下是使用 Scala 和 Scalaz 导出的工作表。

def f(str: Option[String]) = str              //> f: (str: Option[String])Option[String]
f(None)                                       //> res1: Option[String] = None
var x:Option[String]=None                     //> x  : Option[String] = None
x=Some("test")
x                                             //> res2: Option[String] = Some(test)
x=None
x      

现在使用 Scalaz,

def fz(str: Option[String]) = str             //> fz: (str: Option[String])Option[String]
fz(none)                                      //> res4: Option[String] = None
var xz:Option[String]=none                    //> xz  : Option[String] = None
xz=some("test")
xz                                            //> res5: Option[String] = Some(test)
xz=none
xz         

请注意,无论您使用 None 还是 Option.Empty,所有语句的计算方式都是相同的。如何 ?

如您所见,通过 var x:Option[String]=None 语句中的 return 类型让 Scala 知道您的意图很重要。这允许稍后分配 Some。然而,一个简单的 var x=None 将在后面的行中失败,因为这将使变量 x 解析为 None.type 而不是 Option[T].

我认为应该遵循惯例。对于作业,我会选择 var x:Option[String]=None 选项。此外,每当使用 None 时,最好使用 return 类型(在本例中为 Option[String]),这样赋值就不会解析为 None.type.
只有在我无法提供类型并且我需要完成一些任务的情况下,我才会去 Option.empty

鉴于 Option[A].empty 只需 returns None:

/** An Option factory which returns `None` in a manner consistent with
 *  the collections hierarchy.
 */
def empty[A] : Option[A] = None

我会说:

  1. 如您所说,在整个代码库中保持一致。使其保持一致意味着进入您的代码库的程序员可以少担心一件事。 "Should I use None or Option.empty? Well, I see @cdmckay is using X throughout the call base, I'll use that as well"
  2. 可读性 - 想一想什么传达了您最想要的观点。如果你要阅读一个特定的方法,如果它返回一个空的 Option(让我们暂时忽略底层实现只是返回 None 的事实)或一个显式的对你来说更有意义None? IMO,我认为 None 是一个不存在的值,正如文档所指定的那样:

    /** This case object represents non-existent values.
     *
     *  @author  Martin Odersky
     *  @version 1.0, 16/07/2003
     */
    

简答使用 None 如果在将参数传递给任何函数时谈论一个值,则在定义某些内容时使用 Option.empty[T]

var something = Option.empty[String] 表示 something 目前是 None,但将来可以变成 Some("hede")。另一方面 var something = None 没有任何意义。你不能用 Some("hede") 重新分配它,编译器会生气: found : Some[String] required: None.type 所以,这意味着 NoneOption.empty[T] 不是替代品。您可以将 None 传递给任何 Option[T] 但不能将 Some[T] 传递给 None.type

只要有可能,我都会坚持None,这几乎总是如此。它更短并且被广泛使用。 Option.empty 允许你指定底层值的类型,所以当你需要帮助类型推断时使用它。如果编译器已知该类型 None 将按预期工作,但是在定义新变量时

var a = None

会导致将 a 推断为 None.type,这不太可能是您想要的。

然后您可以使用两种方法中的一种来帮助推断您需要什么

@ var a = Option.empty[String]
a: Option[String] = None
@ var a: Option[String] = None
a: Option[String] = None
@ var a = None: Option[String] // this one is rather uncommon
a: Option[String] = None

编译器需要帮助的另一个地方:

List(1, 2, 3).foldLeft(Option.empty[String])((a, e) => a.map(s => s + e.toString))

(代码没有意义,仅作为示例)如果您要省略类型,或将其替换为 None,则累加器的类型将被推断为 Option[Nothing]None.type分别。

就我个人而言,这是我会选择 Option.empty 的地方,对于其他情况,我会尽可能坚持使用 None

正如其他人指出的那样,这更多是个人品味问题,大多数人更喜欢 None,或者在某些情况下,您明确需要输入类型,因为编译器不能' t推断。

这个问题可以外推到其他Scala 类,比如SequencesMapSetList等。在所有这些中,您有几种方法来定义空状态。使用顺序:

  • Seq()
  • Seq.empty
  • Seq.empty[Type]

在第3个中,我更喜欢第2个,因为:

  • 第一个 (Seq()) 容易出错。看起来如果有人想创建一个序列而忘记添加元素
  • 第二个 (Seq.empty) 明确表示希望有一个空序列
  • 虽然第三个 (Seq.empty[Type]) 和第二个一样明确,但更冗长,所以我通常不使用