Scala 为case类自动生成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,对象属性值保留为

如何让scala编译器自动生成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清理编译。