构造 Option 对象的正确方法:Option(value) vs Some(value)

Proper way to construct Option object: Option(value) vs Some(value)

启动Option对象的两种方式的优缺点是什么:

1.

def getAmount: Option[Int] = {
  val a: Int = 1
  Option(a)
}

2.

def getAmount: Option[Int] = {
  val a: Int = 1
  Some(a)
}

我应该使用哪个?

有两个重要的区别。首先,Option 将 return 一个 None 如果它的参数是 null:

scala> val x: Option[String] = Some(null)
x: Option[String] = Some(null)

scala> val y: Option[String] = Option(null)
y: Option[String] = None

这可能很有用,但它并不总是您想要的,并且(同样重要的是)它表明在某些情况下参数有可能为空,这可能会产生误导。

Some 更适用于您想要围绕您知道不为空的值生成 Option 的情况。不幸的是,第二个区别是 Some(foo) 的 return 类型是 Some[Whatever],而不是 Option[Whatever],这在某些情况下非常不方便 Some 推断意味着当您稍后尝试在某些位置使用 NoneOption 时,您会遇到类型错误。在这些情况下,您必须使用 Some(foo): Option[Whatever],这是非常不愉快的。

例如,假设我们有一个表示(我们希望)整数的字符串列表,我们想要对它们进行解析和求和。如果存在解析错误,我们需要 None,否则需要 Some(total)。以下是使用标准库在一次遍历中执行此操作的相当合理的方法:

List("1", "2", "3").foldLeft(Some(0)) {
  case (acc, item) => for {
    t <- acc
    n <- util.Try(item.toInt).toOption
  } yield t + n
}

除非这不起作用——我们收到类型错误:

<console>:10: error: type mismatch;
 found   : Option[Int]
 required: Some[Int]
                  t <- acc
                    ^

我们可以通过写 .foldLeft(Some(0): Option[Int]) 来解决这个问题,但是呃。

这在您的特定示例中不是问题,因为 return 类型明确为 Option[Int],因此您无需担心类型推断。那么 Some(a) 是正确的选择。

附带说明,Scalaz 提供了 somenone 构造函数,可帮助您避免类型推断问题和嘈杂的解决方案,例如 Some(foo): Option[Whatever]:

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> some(10)
res0: Option[Int] = Some(10)

scala> none[Int]
res1: Option[Int] = None

两个return类型都是Option,这使得类型推断更容易。如果你不想使用 Scalaz,你可以自己简单地定义这些:

scala> def some[A](a: A): Option[A] = Some(a)
some: [A](a: A)Option[A]

scala> def none[A]: Option[A] = None
none: [A]=> Option[A]

如果您使用这些而不是 SomeNone,您永远不必担心会推断出不恰当的特定类型。

总结一下:仅在参数可能为空的情况下使用 Option(foo)(理想情况下应该只用于与 Java 的互操作性)。在值已明确键入为 Option 的情况下使用 Some(foo)。如果推断类型为 Some[Whatever],请添加 : Option[Whatever] 类型注释,或使用类似 Scalaz 的 some.

对我来说这只是一个常识问题。当然,您可以想象这样一种情况,即您期望完全属于 Some 类型,而 None 是不允许的。但通常第二种方式看起来更自然:return 类型是 Option,实际实现是 Some(x) 或 None。从技术上讲,从 source code,Option(x) 调用伴随对象的 apply() 方法:

object Option {
  import scala.language.implicitConversions
  implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList
  def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
  def empty[A] : Option[A] = None
}

并且 Some(a) 在案例 class..

上调用 apply() 方法
final case class Some[+A](x: A) extends Option[A] {
  def isEmpty = false
  def get = x
}

其他方法同理。 null 对象的用例在 Travis Brown 答案中得到了很好的解释。