Scala推断:1次评估失败,中间值成功

Scala推断:1次评估失败,中间值成功,scala,type-inference,Scala,Type Inference,我是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) 它起作用了 现在,如果我这样

我是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)
它起作用了

现在,如果我这样做:

val b:Boolean = reverse.map(_.isNaire).getOrElse(false)
它无法编译,类型不匹配:找到任何必需的布尔值


编辑:谢谢铍,通过制作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)
}

注意:scala编译器是2.10.2

scala.Predef
中,有一个从
java.lang.Boolean
scala.Boolean
的隐式转换:

  implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue
def works:Boolean = testValue.map[Boolean](_.naire).getOrElse(false)
因此,在第一种情况下,
val v:Option[Boolean]=reverse.map(u.isNaire)
编译器看到的是一个
java.lang.Boolean
,并在范围内寻找一个隐式方法将其转换为
scala.Boolean
,它可以方便地在
scala.Predef
中找到

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

  • Option[Test]=>Option[java.lang.Boolean]
  • getOrElse[B>:A](默认值:=>B):B
    其中
    A
    java.lang.Boolean
    B
    Any
    ,因为
    scala.Boolean
    不是
    java.lang.Boolean
  • valb:Boolean
    ,编译器找不到从
    Any
    scala.Boolean的隐式转换
  • 解决这个问题的唯一方法是在映射操作期间告诉编译器使用从
    scala.Predef
    java.lang.Boolean
    的隐式转换:

      implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue
    
    def works:Boolean = testValue.map[Boolean](_.naire).getOrElse(false)
    
    这是一个常见问题,经常会弹出,因为
    map
    后跟
    getOrElse
    非常方便。若要在不使用额外类型的情况下正确修复此问题,请在选项上使用
    折叠(变形):

    def worksToo:Boolean = testValue.fold(false)(_.naire)
    
    通过使用
    fold
    您可以获得一些额外的类型安全性,因为没有转换到普通类型。例如,您不能这样做:

    def failsTypeCheck = testValue.fold("test")(_.naire)
    
    虽然编译器对此没有问题:

    def passesTypeCheck = testValue.map(_.naire).getOrElse("test")
    

    java.lang.Boolean
    scala.Boolean
    不一样。为了弥补这个差距,您必须提供一个隐式转换可以完成其工作的位置

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


    • 如果可以从Scala端使用不同的方法,则可以使用隐式值类:
    对象容器{
    隐式类Test2Scala(val-test:test)扩展了AnyVal{
    def naireForScala:Boolean=test.naire
    }
    }
    其他类{
    val testValue=Some(新测试(真))
    进口集装箱_
    def神秘编译错误:布尔=
    testValue.map(u.naireForScala).getOrElse(false)
    }
    
    这在运行时不需要额外的实例。它只是提供了另一种方法来丰富Java类


    • 如果可以派生子类,则可以使用
      DummyImplicit
      ,保留方法的名称:
    类Test2(\u-naire:Boolean)扩展了Test(\u-naire){ def naire(隐式di:dummy隐式):布尔=\u naire } 其他类{ val testValue=Some(新的Test2(true)) def神秘编译错误:布尔= testValue.map(u.naire).getOrElse(false) }
    要获得不同的方法签名,需要使用
    DummyImplicit
    。这有点棘手,在运行时需要一个额外的实例,但是
    Test2
    是一个
    Test
    (就OOP而言)


    • 将Java实例包装到Scala实例中:
    类TestWrapper(test:test){
    def naire:Boolean=test.naire
    }
    其他类{
    val testValue=Some(新TestWrapper(新测试(true)))
    def神秘编译错误:布尔=
    testValue.map(u.naire).getOrElse(false)
    }
    

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

    我可以编译一个修改过的版本,所以请提供一个。您最好在map()函数文本中将java.lang.Boolean转换为scala.Boolean,因为
    false
    不是
    java.lang.Boolean
    的值。例如,
    def神秘编译错误:Boolean=testValue.map(u.naire==true).getOrElse(false)
    IntelliJ IDEA提醒我,
    .map(u.predicate).getOrElse(false)
    可以简化为
    .exists(u.predicate)
    ,这比对布尔人使用
    fold
    更好@很好,它告诉了我同样的事情,我只是没有注意到:)