Scala 为case类自动生成case对象
如何让scala编译器自动生成case对象Scala 为case类自动生成case对象,scala,case-class,scala-macros,companion-object,scalameta,Scala,Case Class,Scala Macros,Companion Object,Scalameta,如何让scala编译器自动生成case对象 // Pizza class class Pizza (val crust_type: String) // companion object object Pizza { val crustType = "crust_type" } 案例对象所需的属性 对于案例类中的每个属性在案例对象中生成一个属性 将每个对应案例对象的值设置为属性名称的字符串表示形式,并将对象属性名称的camelCase更改为snake\u case,对象属性值保留为
// Pizza class
class Pizza (val crust_type: String)
// companion object
object Pizza {
val crustType = "crust_type"
}
案例对象所需的属性
- 对于
在案例类中的每个属性
案例对象中生成一个属性
- 将每个对应案例对象的值设置为属性名称的字符串表示形式,并将对象属性名称的
更改为camelCase
,对象属性值保留为snake\u case
snake\u case
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)
}
并使用它
@GenerateCompanion
class Pizza(val crust_type: String)
Pizza.crustType //crust_type
新宏:
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)
}
用法:
@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
也许是一个代替宏的好工具?如果我想为某些属性指定一个手动指定的名称,或者对象中的属性比case类中的属性多,那么宏是否有点类似?这是否也可行?例如,将参数传递给注释。我想您可以。但是为什么需要注释参数呢?您只需在companion对象中手动创建所需的其他成员。然后应该修改这个宏。现在,它不期望现有的伴生对象并创建它。宏应该修改现有的伴生对象,或者在它不存在的情况下创建它。实际上,这是一个很好的建议。也许你可以相应地更新答案。太棒了。我是否理解正确,如果伴生对象不存在,将创建一个新的伴生对象,如果存在伴生对象,则只会对其进行充实?@GrigorievNick For me它在2.11.12中编译。IntelliJ抱怨
Seq[ValDef]
/Seq[Tree]
但scalac编译。执行sbt清理编译。