2

我是 scala 的初学者,不明白这里发生了什么:

给定:

val reverse:Option[MyObject] = ...

myObject.isNaire返回布尔值。

如果我做 :

val v:Option[Boolean] =  reverse.map(_.isNaire)
val b:Boolean = v.getOrElse(false)

这行得通。

现在,如果我这样做:

val b:Boolean = reverse.map(_.isNaire).getOrElse(false)

它无法编译type mismatch: found Any, required Boolean


编辑:感谢 Beryllium,通过制作 SSCCE,我找到了解释的开始。在第一个示例中,myObject 是一个 java 类,因此 isNaire 是一个 java.lang.Boolean。我认为隐式转换应该使它透明,所以仍然欢迎解释。

class Test(val naire:java.lang.Boolean)

class Other {
  val testValue = Some(new Test(true))
  def mysteriousCompilationError:Boolean = testValue.map(_.naire).getOrElse(false)
}

注意:ScalaCompiler 是 2.10.2

4

2 回答 2

5

在有一个从toscala.Predef的隐式转换:java.lang.Booleanscala.Boolean

  implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue

因此,在您的第一种情况下val v:Option[Boolean] = reverse.map(_.isNaire),编译器会看到 ajava.lang.Boolean并在范围内寻找一个隐式方法将其转换为 a scala.Boolean,它可以方便地在scala.Predef.

在第二种情况下,testValue.map(_.naire).getOrElse(false)编译器按以下顺序执行操作:

  1. Option[Test] => Option[java.lang.Boolean]
  2. getOrElse[B >: A](default: => B): Bwhere Ais java.lang.Booleanand Bis Anysince scala.Booleanis not>: java.lang.Boolean
  3. val b:Boolean, 编译器找不到从Anyto的隐式转换scala.Boolean

解决这个问题的唯一方法是在映射操作期间告诉编译器使用隐式转换 from scala.Predefto go from java.lang.Booleanto scala.Boolean

def works:Boolean = testValue.map[Boolean](_.naire).getOrElse(false)

这是一个常见的问题,并且经常弹出,因为map随后getOrElse非常方便。要在没有额外类型的情况下正确解决此问题,fold请在选项上使用 (catamorphism):

def worksToo:Boolean = testValue.fold(false)(_.naire)

通过使用fold,您可以获得一些额外的类型安全性,因为没有转换为常见类型。例如,您不能这样做:

def failsTypeCheck = testValue.fold("test")(_.naire)

虽然编译器对此没有问题:

def passesTypeCheck = testValue.map(_.naire).getOrElse("test")
于 2013-08-04T19:46:28.397 回答
0

java.lang.Booleanscala.Boolean不一样。为了弥合差距,您必须提供隐式转换可以工作的位置。

有一些模式可以处理这些类型的 Java/Scala 互操作性问题:


  • 如果可以从 Scala 端使用不同的方法,则可以使用隐式值类:
object Container {
  implicit class Test2Scala(val test: Test) extends AnyVal {
    def naireForScala: Boolean = test.naire
  }
}

class Other {
  val testValue = Some(new Test(true))

  import Container._
  def mysteriousCompilationError: Boolean = 
    testValue.map(_.naireForScala).getOrElse(false)
}

这在运行时不需要额外的实例。它只是提供了另一种方法来丰富 Java 类。


  • 如果您可以派生子类,则可以使用以下方法保留方法的名称DummyImplicit
class Test2(_naire: Boolean) extends Test(_naire) {
  def naire(implicit di: DummyImplicit): Boolean = _naire
}

class Other {
  val testValue = Some(new Test2(true))
  def mysteriousCompilationError: Boolean = 
    testValue.map(_.naire).getOrElse(false)
}

DummyImplicit需要获得不同的方法签名。这有点棘手,在运行时需要一个额外的实例,但是Test2是一个Test(就 OOP 而言)。


  • 将 Java 实例包装在 Scala 实例中:
class TestWrapper(test: Test) {
  def naire: Boolean = test.naire
}

class Other {
  val testValue = Some(new TestWrapper(new Test(true)))

  def mysteriousCompilationError: Boolean = 
    testValue.map(_.naire).getOrElse(false)
}

需要一个额外的实例,你必须添加委托,TestWrapper不是一个Test,但它很简单。

于 2013-08-04T21:13:36.813 回答