У меня есть следующий код:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
def main(args: Array[String]): Unit = {
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
Это вызывает StackOverflowError
при запуске.
Если я добавлю lazy
перед writesT
, StackOverflowError
исчезнет, и все будет работать:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
implicit val writesA: OWrites[A] = Json.writes[A]
implicit lazy val writesT: OWrites[T] = Json.writes[T]
def main(args: Array[String]): Unit = {
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
StackOverflowError
также исчезает, когда я перемещаю implicit
s в функцию main
:
import play.api.libs.json._
object Test {
sealed trait T
case class A(s: String) extends T
def main(args: Array[String]): Unit = {
implicit val writesA: OWrites[A] = Json.writes[A]
implicit val writesT: OWrites[T] = Json.writes[T]
val x = A("str")
println(Json.toJson[T](x)(writesT))
}
}
Может кто-нибудь объяснить мне, почему я получаю StackOverflowError
в первом случае?
Я подозреваю, что это как-то связано с порядком инициализации и макросами, которые play-json использует в фоновом режиме. Но если это так, я не понимаю, почему использование lazy
помогает, потому что код должен все еще генерироваться во время компиляции, и простая оценка его позже во время выполнения не должна ничего менять. По-видимому, в более поздних случаях экземпляр writesA
найден writesT
, но не в первом случае. Почему добавление lazy
решает проблему времени компиляции с разрешением последствий и генерацией макрокода?
Или это проблема совершенно другого уровня?
Я использую Scala 2.12.3 и play-json 2.6.2.