我有以下代碼,它使用spray-json通過parseJson
方法將一些JSON反序列化爲一個case類。Scala在解決implicits時如何使用顯式類型?
根據隱式JsonFormat [MyCaseClass]被定義,其中(在線或從同伴對象導入),以及是否存在被定義,當它提供一個明確的類型,代碼可能不能編譯。
我不明白爲什麼從伴侶對象導入隱式要求它在定義時具有顯式類型,但如果我將它放在內聯中,情況並非如此?
有趣的是,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])
}
這是其中一個「當我這樣做時會感到痛苦」的問題,其中最好的答案几乎肯定是「不要那麼做」。以我的經驗,缺乏類型註釋的隱式值是Scala中最常見的混淆源,行爲中奇怪的跨版本差異等。 –
嗨特拉維斯 - 確實,這是一個有趣的錯誤工作,但我想下一次類型註釋將成爲我的第一個類似的問題呼叫端口!不知道這是否算作Scala bug,但可能會把某些東西放到郵件列表中,以防萬一。 – MrProper
編譯器吐出一條錯誤消息,說'隱式方法不適用於此,因爲它是在應用程序點之後出現的,它缺少顯式結果類型,所以至少該錯誤對於診斷和修復來說是微不足道的。「 – Hugh