【问题标题】:Scala - Option.getOrElse() "type erasure"?Scala - Option.getOrElse()“类型擦除”?
【发布时间】:2012-05-28 20:50:02
【问题描述】:

我正在尝试使用 Option.getOrElse() 方法,但它返回 etiher Any 或 ScalaObject 而不是 Option 参数化的正确类的实例。我找不到有关此问题的任何提及,而且似乎不应该存在。我做错了什么?

class MyClass {

  def isOk = true

}

val myVal = Some(new MyClass) // :Option[MyClass]

val check = myVal.getOrElse(false).isOk

无法调用isOk 方法,因为它尝试在Any 上调用它。

【问题讨论】:

  • 类型“擦除”不是这里的问题,而是子类型带来的极端表达能力有时过于宽松,无法捕捉到你的错误。

标签: scala option type-erasure


【解决方案1】:

您正在尝试在MyClass 和Boolean (Any) 的基类上调用方法isOk。

试试这个:

scala> class MyClass(b: Boolean) { def isOk = b }
defined class MyClass

scala> val myVal = Some(new MyClass(true))
myVal: Some[MyClass] = Some(MyClass@35d56bbe)

scala> myVal.map{_.isOk}.getOrElse(false)
res0: Boolean = true

scala> myVal.getOrElse(new MyClass(false)).isOk
res1: Boolean = true

【讨论】:

  • 我认为map (_.isOk) 方法是 noncom 真正想要做的。
【解决方案2】:

按设计工作。这个表达式:

myVal.getOrElse(false)

返回未包装的MyClass 实例或(如果Option 实际上是None)-false。 MyClass 和 Boolean 的唯一常见类型是...Any。这就是你所看到的。

为了让它工作,你必须从getOrElse()返回与MyClass兼容的东西:

myVal.getOrElse(new MyClass).isOk

或者也许你想实现 null-object 模式:

object MyClass {
  val Empty = new MyClass
}

myVal.getOrElse(MyClass.Empty).isOk

【讨论】:

  • 如果你返回一个新的MyClass,那么表达式将永远为真
  • @LuigiPlinge:当然,这是关于一般概念的(这就是我提到空对象模式的原因——但它遇到了同样的问题)。事实上,我认为map (_.isOk) 实际上是 OP 想要的。
【解决方案3】:

您正在使用布尔值在 Option[MyClass] 中应用 getOrElse,因此,它们的共同超类是 Any。

你应该模式匹配:

val check = myVal match {
  case Some(c) => c.isOk
  case None => false
} 

【讨论】:

  • +1 在处理选项时,我总是鼓励模式匹配,因为它使你可以用它们做的所有事情都变得非常简单明了。
【解决方案4】:

您正在通过Option[MyClass] 呼叫getOrElse。您将Boolean 作为参数传递给getOrElse。发生的情况是 Scala 将选项转换为 Option[Any],因为 Any 是 MyClass 和 Boolean 的最常见类型。

将MyClass(或MyClass 的子类)传递给getOrElse,而不是false。

【讨论】:

    【解决方案5】:

    因为您的 getOrElse 可能返回 false,所以 MyClass 和 false 的常见类型是 Any。

    【讨论】:

      【解决方案6】:

      Jhonny Everson 是对的。模式匹配就是答案。在这种情况下,他的答案中的模式相当于exists

      scala> Some(new MyClass) :: None :: Nil map(_.exists(_.isOK))
      res12: List[Boolean] = List(true, false)
      

      【讨论】:

        【解决方案7】:

        所以你有一个Option[A],一个函数A => B,以及一个默认的B,当可选值为None时,你想以B结束。 (在您的情况下,A 是 MyClass 和 B 是 Boolean)。

        作为一名 Haskeller,我想到的第一件事就是胡闹。回想一下,在 Haskell 中,Option 被称为 Maybe。所以我们hoogle Maybe a -> (a -> b) -> b -> b,最热门的是maybe :: b -> (a -> b) -> Maybe a -> b,这正是我们想要的。

        data MyClass = MyClass { isOK :: Bool }
        newMyClass = MyClass { isOK = true }
        
        myVal = newMyClass
        check = maybe False isOK myVal
        

        这很好,但是 Scala 呢?好吧,相当于 hoogle 的 Scala 是Scalex。我在 Scalex 中搜索Option[A] => B => (A => B) => B,但无济于事。因此,让我们看看 maybe 函数是如何在 Haskell 中实现的。您可以通过 hoogle 的相应链接找到源代码。

        maybe :: b -> (a -> b) -> Maybe a -> b
        maybe n _ Nothing  = n
        maybe _ f (Just x) = f x
        

        似乎很容易翻译成 Scala

        def option[A, B](opt: Option[A])(n: B)(f: A => B) = opt match {
          case None => n
          case Some(x) => f(x)
        }
        

        然后可以这样使用:

        val check = option(myVal)(false)(_.isOK)
        

        如果你想用更少的柯里化或拉皮条 Option 类来做到这一点,你将不得不询问比我更精通 Scala 的人,但请注意这基本上归结为 Jhonny Everson 建议的模式匹配。

        【讨论】:

          猜你喜欢
          • 2020-07-27
          • 2012-08-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-30
          • 1970-01-01
          相关资源
          最近更新 更多