我面临这个错误:
exception during macro expansion:
scala.ScalaReflectionException: type T is not a class
at scala.reflect.api.Symbols$SymbolApi.asClass(Symbols.scala:284)
at scala.reflect.api.Symbols$SymbolApi.asClass$(Symbols.scala:284)
at scala.reflect.internal.Symbols$SymbolContextApiImpl.asClass(Symbols.scala:99)
at play.api.libs.json.JsMacroImpl.directKnownSubclasses$1(JsMacroImpl.scala:527)
at play.api.libs.json.JsMacroImpl.macroImpl(JsMacroImpl.scala:850)
at play.api.libs.json.JsMacroImpl.implicitConfigWritesImpl(JsMacroImpl.scala:44)
当尝试声明一个方法来为给定的泛型类型创建编写器时:
def makeWriter[T]: Writes[T] = Json.writes[T]
现在
Json.writes
的实现只是一个宏,它不需要任何类型的上下文边界或其他东西:
def writes[A]: OWrites[A] = macro JsMacroImpl.withOptionsWritesImpl[A]
有人知道这是怎么回事吗?我不太擅长宏,所以如果有人能解释这一点,我将不胜感激,任何解决方案都会受到赞赏。
是的,这是可能的。
堆栈跟踪
scala.ScalaReflectionException: type T is not a class
at scala.reflect.api.Symbols$SymbolApi.asClass(Symbols.scala:284)
at scala.reflect.api.Symbols$SymbolApi.asClass$(Symbols.scala:284)
at scala.reflect.internal.Symbols$SymbolContextApiImpl.asClass(Symbols.scala:99)
at play.api.libs.json.JsMacroImpl.directKnownSubclasses$1(JsMacroImpl.scala:555)
at play.api.libs.json.JsMacroImpl.macroImpl(JsMacroImpl.scala:869)
at play.api.libs.json.JsMacroImpl.implicitConfigWritesImpl(JsMacroImpl.scala:44)
提示问题所在:
def directKnownSubclasses: Option[List[Type]] = {
// Workaround for SI-7046: https://issues.scala-lang.org/browse/SI-7046
val tpeSym = atag.tpe.typeSymbol.asClass
...
.asClass
只能在类符号上调用,否则会抛出异常(宏的运行时异常是主代码的编译错误)
def asClass: ClassSymbol = throw new ScalaReflectionException(s"$this is not a class")
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/api/Symbols.scala#L284
定义的问题
def makeWriter[T]: Writes[T] = Json.writes[T]
是宏
Json.writes[T]
在这里扩展,其中T
还不是一个类,而只是一个类型参数。如果您也将 makeWriter
设为宏,则可以推迟宏扩展
import play.api.libs.json.Writes
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def makeWriter[T]: Writes[T] = macro makeWriterImpl[T]
def makeWriterImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val typeT = weakTypeOf[T]
q"_root_.play.api.libs.json.Json.writes[$typeT]"
}
// in a different subproject
case class A(i: Int)
makeWriter[A] // compiles