如果您运行此简单代码,您将看到以下内容:
object A {
println("from A")
var x = 0
}
object B {
println("from B")
A.x = 1
}
object Test extends App {
println(A.x)
}
// Result:
// from A
// 0
你可以猜到,scala懒洋洋地初始化对象。对象B未初始化,它的工作方式与预期不符。我的问题是:我可以使用技巧来初始化对象B而无需访问它?我可以使用的第一个技巧是扩展具有一些特征的对象,并使用反射来初始化扩展特定特征的对象。我认为更优雅的方法是使用宏注释来注释对象:
@init
object B {
println("from B")
A.x = 1
}
class init extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro init.impl
}
object init {
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
// what should i do here ?
}
}
但我有点困惑。如何在宏impl
方法中从带注释的对象调用方法(以便初始化)?
答案 0 :(得分:2)
我找到了解决方案:
<强>用法:强>
object A {
println("from A")
var x = 0
}
@init
object B {
println("from B")
A.x = 1
}
@init
object C {
println("from C")
A.x = 2
}
object Test extends App {
init()
println(A.x)
}
<强>输出:强>
from B
from A
from C
2
宏实施:
class init extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro init.impl
}
object init {
private val objFullNames = new mutable.MutableList[String]()
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
annottees.map(_.tree).toList match {
case (obj: ModuleDef) :: Nil =>
objFullNames += c.typecheck(obj).symbol.fullName
case _ =>
c.error(c.enclosingPosition, "@init annotation supports only objects")
}
annottees.head
}
def apply() = macro runImpl
def runImpl(c: whitebox.Context)(): c.Expr[Any] = {
import c.universe._
val expr = objFullNames.map(name => q"${c.parse(name)}.##").toList
val res = q"{..$expr}"
c.Expr(res)
}
}