问题
How can I have the scala compiler automatically generate the case object?
// Pizza class
class Pizza (val crust_type: String)
// companion object
object Pizza {
val crustType = "crust_type"
}
Desired properties for case object
- for each attribute in the
case class
generate an attribute in thecase object
- set the value in of each corresponding case object to the string representation of the attribute name and change
camelCase
tosnake_case
for the object attribute name, keepsnake_case
for object attribute value
回答1:
You can create macro annotation
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("enable macro paradise to expand macro annotations")
class GenerateCompanion extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GenerateCompanion.impl
}
object GenerateCompanion {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
val vals = paramss.flatten.map(p => {
val name = p.name.toString
q"val ${TermName(underscoreToCamel(name))}: String = $name"
})
q"""
$c
object ${tpname.toTermName} {..$vals}
"""
}
}
def underscoreToCamel(name: String): String = "_([a-z\\d])".r.replaceAllIn(name, _.group(1).toUpperCase)
}
and use it
@GenerateCompanion
class Pizza(val crust_type: String)
Pizza.crustType //crust_type
New macro:
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("enable macro paradise to expand macro annotations")
class GenerateCompanion extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GenerateCompanion.impl
}
object GenerateCompanion {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
def vals(paramss: Seq[Seq[ValDef]]): Seq[ValDef] =
paramss.flatten.map(p => {
val name = p.name.toString
q"val ${TermName(underscoreToCamel(name))}: String = $name"
})
annottees match {
case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
q"""
$c
object ${tpname.toTermName} {
..${vals(paramss)}
}
"""
case (c@q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") ::
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
q"""
$c
$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
..$body
..${vals(paramss)}
}
"""
}
}
def underscoreToCamel(name: String): String = "_([a-z\\d])".r.replaceAllIn(name, _.group(1).toUpperCase)
}
Usage:
@GenerateCompanion
class Pizza(val crust_type: String, val foo_foo: Int)
object Pizza {
def bar: String = "bar"
}
Pizza.crustType //crust_type
Pizza.fooFoo //foo_foo
Pizza.bar //bar
来源:https://stackoverflow.com/questions/51048310/automatically-generate-case-object-for-case-class