是否可以替换为Scala宏中的案例类生成的默认apply方法?

是否可以替换为Scala宏中的案例类生成的默认apply方法?,scala,scala-macros,case-class,scala-macro-paradise,Scala,Scala Macros,Case Class,Scala Macro Paradise,这似乎不起作用(使用2.11.1和macro paradise 2.0.1)。 我希望case类生成的方法要么被抑制,要么在树中,这样我就可以摆脱它。这是一个硬限制吗 class evis extends StaticAnnotation { def macroTransform(annottees: Any*) = macro EvisMacro.impl } object EvisMacro { def impl(c: blackbox.Context)(annottees: c

这似乎不起作用(使用2.11.1和macro paradise 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)方法应用定义了两次 冲突符号均源自文件“core/src/main/scala/Test.scala” @埃维斯
^

对于编译器来说,宏注释生成的代码与手工编写的代码没有任何区别,这是造成问题的原因。如果您手动编写由示例中提供的宏生成的代码,您将得到完全相同的双定义错误,因此这不是错误-这是案例类合成的限制。不幸的是,案例类合成是不可扩展的,因此需要解决这个问题

我建议的一个解决方法是从类的mods中删除
CASE
标志,然后宏可以完全自由地选择要生成的成员。然而,这意味着宏必须生成case类通常生成的所有代码,这将不是一件令人愉快的事情。这里的另一个警告是,编译器通过发出更高效的代码,特别是在
CASE
类上处理模式匹配,因此这种模拟也会损失一些性能(我认为损失将是微乎其微的,甚至可能不存在Scala 2.11中新的基于名称的模式匹配机制,但这需要测试)