Scala 在解析隐式时如何使用显式类型?

How does Scala use explicit types when resolving implicits?

我有以下代码,它使用 spray-json 通过 parseJson 方法将一些 JSON 反序列化为 case class。

根据隐式 JsonFormat[MyCaseClass] 的定义位置(内联或从伴随对象导入),以及定义时是否提供显式类型,代码可能无法编译。

我不明白为什么从伴生对象中导入implicit 需要它在定义时有显式类型,但如果我把它内联,就不是这样了?

有趣的是,IntelliJ 在所有情况下都能正确定位隐式参数(通过 cmd-shift-p)。

我正在使用 Scala 2.11.7。

损坏的代码 - 从伴随对象导入通配符,推断类型:

import SampleApp._
import spray.json._

class SampleApp {
  import MyJsonProtocol._
  val inputJson = """{"children":["a", "b", "c"]}"""
  println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}

object SampleApp {
  case class MyCaseClass(children: List[String])

  object MyJsonProtocol extends DefaultJsonProtocol {
    implicit val myCaseClassSchemaFormat = jsonFormat1(MyCaseClass)
  }
}

结果:

Cannot find JsonReader or JsonFormat type class for SampleAppObject.MyCaseClass

请注意,显式导入 myCaseClassSchemaFormat 隐式也会发生同样的事情。

工作代码 #1 - 从伴随对象导入通配符,显式类型:

向伴随对象中的 JsonFormat 添加显式类型会导致代码编译:

import SampleApp._
import spray.json._

class SampleApp {
  import MyJsonProtocol._
  val inputJson = """{"children":["a", "b", "c"]}"""
  println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}

object SampleApp {
  case class MyCaseClass(children: List[String])

  object MyJsonProtocol extends DefaultJsonProtocol {
    //Explicit type added here now
    implicit val myCaseClassSchemaFormat: JsonFormat[MyCaseClass] = jsonFormat1(MyCaseClass)
  }
}

工作代码 #2 - 隐式内联,推断类型:

然而,将隐式参数放在使用它们的地方,没有显式类型,也可以!

import SampleApp._
import spray.json._

class SampleApp {
  import DefaultJsonProtocol._

  //Now in-line custom JsonFormat rather than imported
  implicit val myCaseClassSchemaFormat = jsonFormat1(MyCaseClass)

  val inputJson = """{"children":["a", "b", "c"]}"""
  println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}

object SampleApp {
  case class MyCaseClass(children: List[String])
}

在搜索 Huw 在他的评论中提到的错误消息后,我找到了这个 2010 年的 Whosebug 问题:Why does this explicit call of a Scala method allow it to be implicitly resolved?

这让我想到了 2008 年创建并于 2011 年关闭的 Scala 问题:https://issues.scala-lang.org/browse/SI-801 ('require explicit result type for implicit conversions?')

马丁表示:

I have implemented a slightly more permissive rule: An implicit conversion without explicit result type is visible only in the text following its own definition. That way, we avoid the cyclic reference errors. I close for now, to see how this works. If we still have issues we migth come back to this.

这成立 - 如果我重新排序中断代码以便首先声明伴生对象,然后代码编译。 (还是有点奇怪!)

(我怀疑我没有看到 'implicit method is not applicable here' 消息,因为我有一个隐式值而不是转换 - 虽然我在这里假设根原因同上)。