使用过的游戏框架,我的文件带有 case类(在Project中具有聚合任务。这一点很重要),例如
case class Project (var tasks: Map[String, Task])
case class Task (var activities: Map[String, String])
以及另一个用于转换
的文件 traittrait JsonConverters {
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
implicit val taskWrite: OWrites[Task] = Json.writes[Task]
implicit val taskRead: Reads[Task] = Json.reads[Task]
}
object JsonConverters extends JsonConverters
在我的服务类别中,类似
import support.JsonConverters._
class Service {
def someMethod(json: String) = {
val obj = Json.parse(json).as[Map[String, Project]]
}
}
在编译时一切正常。但是在json解析中我在json库中得到了NPE
(play.api.libs.json.DefaultReads#mapReads
)
implicit def mapReads[K, V](k: String => JsResult[K])(implicit fmtv: Reads[V]): Reads[Map[K, V]] = Reads[Map[K, V]] {
implicit fmtv: Reads[V]
为空(对于Task类)(对于Project全部正常)
我认为是因为在.as[Map[String, Project]
中仅指定了Project类,而不指定了Task。但这不完全是
我为此问题找到了2种不同的解决方案,但这两种方法对我来说都是丑陋的
1)在具有案例类的相同文件中声明所有隐式转换器。但我想将所有转换器都放在单独的文件中。此解决方案不适合我
2)在trait JsonConverters
trait JsonConverter {
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
implicit lazy val taskWrite: OWrites[Task] = Json.writes[Task]
implicit lazy val taskRead: Reads[Task] = Json.reads[Task]
}
它工作正常,并且看起来不错。但是我不理解,为什么在单独特征中没有懒惰不能正常工作?有人可以描述吗?或建议其他解决方案
答案 0 :(得分:2)
琐碎变量(val
)和惰性变量(lazy val
)的初始化顺序是不同的。
val
的初始化顺序是文件中描述的顺序。
lazy val
在首次访问时被初始化。
在您的情况下,Project
包含一个Task
,因此projectRead
需要完成初始化的taskRead
。
在您的代码中,没有lazy
的情况下,projectRead
在taskRead
之前声明,因此taskRead
在null
需要时仍projectRead
。这就是为什么您要使用NPE。
仍然,编译器可以找到隐式taskRead
且不会引发错误。
您对lazy
的修复之所以有效,是因为taskRead
现在在首次访问时初始化 。现在,taskRead
在projectRead
要求时进行初始化。 NPE不见了。
您可以通过切换声明顺序来实现相同的解决方法:
trait JsonConverter {
implicit val taskWrite: OWrites[Task] = Json.writes[Task]
implicit val taskRead: Reads[Task] = Json.reads[Task]
implicit val projectWrite: OWrites[Project] = Json.writes[Project]
implicit val projectRead: Reads[Project] = Json.reads[Project]
}