为 case class 自动生成 case 对象

automatically generate case object for case class

如何让 scala 编译器自动生成 case 对象?

// Pizza class
class Pizza (val crust_type: String)

// companion object
object Pizza {
    val 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._

    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