似乎这不起作用(使用2.11.1和宏天堂2.0.1)。 我希望case类生成的方法要么被抑制,要么在树中,所以我可以摆脱它。这是一个很难的限制吗?
class evis extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro EvisMacro.impl
}
object EvisMacro {
def impl(c: blackbox.Context)(annottees: c.Expr[Any]*) : c.Expr[Any] = {
import c.universe._
def makeApply(tpName: TypeName, parents: List[Tree], params: List[List[ValDef]] ) : List[Tree]= {
List(q"""def apply(...$params): $tpName = null""")
}
val result = annottees map (_.tree) match {
case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
:: Nil if mods.hasFlag(Flag.CASE) =>
c.info(c.enclosingPosition, s"Eviscerating $tpname !($mods, $parents, $paramss)", true)
parents match {
case q"${pname: TypeName}" :: rest =>
c.info(c.enclosingPosition, s"${pname.decodedName}", true )
val sc = c.universe.rootMirror.staticClass( pname.decodedName.toString )
c.info(c.enclosingPosition, s"${sc}", true )
}
val name = tpname.toTermName
q"""
$classDef
object $name {
..${makeApply(tpname, parents, paramss)}
}
"""
case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }")
:: q"object $objName {..$objDefs}"
:: Nil if mods.hasFlag(Flag.CASE) =>
q"""
$classDef
object $objName {
..${makeApply(tpname, parents, paramss)}
..$objDefs
}
"""
case _ => c.abort(c.enclosingPosition, "Invalid annotation target: must be a case class")
}
c.Expr[Any](result)
}
}
使用它:
trait Thing
@evis
case class Trade(id: Long, notional: Long, comment: String) extends Thing
@evis
case class Pop(name: String) extends Thing
object Pop{
}
object TestTrade extends App{
val t = Trade (1, 1, "")
val p : Pop = Pop("")
println(t)
}
结果:
错误:(2,2)方法apply被定义两次 冲突的符号都起源于文件' core / src / main / scala / Test.scala' @evis ^
答案 0 :(得分:3)
问题是由于这样一个事实,即对于编译器,宏注释生成的代码与手工编写的代码没有任何不同。如果您手动编写示例中提供的宏生成的代码,您将获得完全相同的双定义错误,因此它不是错误 - 它是案例类合成的限制。不幸的是,案例类合成是不可扩展的,因此需要解决这个问题。
我建议的一个解决方法是从类的mod中删除CASE
标志,然后宏可以完全自由地选择要生成的成员。但是,这意味着宏必须生成案例类通常生成的所有代码,这不会非常令人愉快。另一个需要注意的是,编译器通过发出更高效的代码来处理CASE
类上的模式匹配,因此这样的仿真也会失去一些性能(我认为损失将是微不足道的,甚至可能是非 - 与Scala 2.11中基于名称的新模式匹配机制一致 - 但需要对其进行测试。)