【问题标题】:Proper way to construct Option object: Option(value) vs Some(value)构造 Option 对象的正确方法:Option(value) vs Some(value)
【发布时间】:2015-04-11 15:09:55
【问题描述】:

两种发起Option对象的方式的优缺点是什么:

1.

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

2.

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

我应该使用哪个?

【问题讨论】:

    标签: scala scala-option


    【解决方案1】:

    有两个重要的区别。首先,Option 将返回一个 None 如果其参数为空:

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

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

    Some 更适合您想要围绕一个您知道不为空的值生成Option 的情况。不幸的是,第二个区别是Some(foo) 的返回类型是Some[Whatever],而不是Option[Whatever],这在某些情况下可能非常不方便,因为推断出Some 意味着当您尝试使用@ 时会出现类型错误987654331@ 或 Option 稍后在某些位置。在这些情况下,您必须使用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]) 来解决这个问题,但是啊。

    在您的具体示例中这不是问题,因为返回类型明确为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
    

    两种返回类型都是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 之类的东西。

    【讨论】:

      【解决方案2】:

      对我来说,这只是一个常识问题。当然,您可以想象这样的情况,您期望某些类型的东西是 Some and None 是不允许的。但通常第二种方式看起来更自然:返回类型是 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) 在案例类上调用 apply() 方法..

      final case class Some[+A](x: A) extends Option[A] {
        def isEmpty = false
        def get = x
      }
      

      所有其他方法都相同。 Travis Brown 答案中很好地解释了 null 对象的用例。

      【讨论】:

      • 他们的行为有所不同——试试Some[String](null)Option[String](null)
      • 您对他们的初始化完全正确,并且您在回答中非常清楚地解释了这个用户案例!我将编辑我的答案以使其准确。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-12
      • 2020-09-09
      • 1970-01-01
      • 2014-03-18
      相关资源
      最近更新 更多