使用方法(字段=方法)为案例 class 生成伴生对象
Generate companion object for case class with methods (field = method)
使用 scala-macros
为案例 class 生成伴随对象
我试过的一些代码示例,它有效我可以获得元组列表(名称 -> 类型)但是如何在同一范围内生成对象?
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.decls.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
} .get
.paramLists
.head
val extractParams = fields.map { field =>
val name = field.asTerm.name
val fieldName = name.decodedName.toString
val NullaryMethodType(fieldType) = tpe.decl(name).typeSignature
c.Expr[List[(String, String)]](
q"""
($fieldName, ${fieldType.toString})
"""
)
是否可以对某些情况进行注释 class 并且生成的同伴在同一范围内可见?
// test case: defined a class with some fields
@GenerateCompanionWithFields
case class SomeCaseClass(i: Int, b: Byte, c: Char)
目标:
SomeCaseClass.i() // returns Int
SomeCaseClass.b() // returns Byte
SomeCaseClass.c() // returns Char
您的代码似乎适用于 def macros. But if you want to generate companion you should use macro annotations
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise")
class GenerateCompanionWithFields extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro Macro.impl
}
object Macro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (cls @ q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
val newMethods = paramss.flatten.map {
case q"$_ val $tname: $tpt = $_" =>
q"def $tname(): String = ${tpt.toString}"
}
q"""
$cls
object ${tpname.toTermName} {
..$newMethods
}
"""
}
}
}
}
import Macros._
object App {
@GenerateCompanionWithFields
case class SomeCaseClass(i: Int, b: Byte, c: Char)
}
//Warning:scalac: {
// case class SomeCaseClass extends scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val i: Int = _;
// <caseaccessor> <paramaccessor> val b: Byte = _;
// <caseaccessor> <paramaccessor> val c: Char = _;
// def <init>(i: Int, b: Byte, c: Char) = {
// super.<init>();
// ()
// }
// };
// object SomeCaseClass extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def i(): String = "Int";
// def b(): String = "Byte";
// def c(): String = "Char"
// };
// ()
//}
使用 scala-macros
为案例 class 生成伴随对象我试过的一些代码示例,它有效我可以获得元组列表(名称 -> 类型)但是如何在同一范围内生成对象?
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.decls.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
} .get
.paramLists
.head
val extractParams = fields.map { field =>
val name = field.asTerm.name
val fieldName = name.decodedName.toString
val NullaryMethodType(fieldType) = tpe.decl(name).typeSignature
c.Expr[List[(String, String)]](
q"""
($fieldName, ${fieldType.toString})
"""
)
是否可以对某些情况进行注释 class 并且生成的同伴在同一范围内可见?
// test case: defined a class with some fields
@GenerateCompanionWithFields
case class SomeCaseClass(i: Int, b: Byte, c: Char)
目标:
SomeCaseClass.i() // returns Int
SomeCaseClass.b() // returns Byte
SomeCaseClass.c() // returns Char
您的代码似乎适用于 def macros. But if you want to generate companion you should use macro annotations
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise")
class GenerateCompanionWithFields extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro Macro.impl
}
object Macro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (cls @ q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
val newMethods = paramss.flatten.map {
case q"$_ val $tname: $tpt = $_" =>
q"def $tname(): String = ${tpt.toString}"
}
q"""
$cls
object ${tpname.toTermName} {
..$newMethods
}
"""
}
}
}
}
import Macros._
object App {
@GenerateCompanionWithFields
case class SomeCaseClass(i: Int, b: Byte, c: Char)
}
//Warning:scalac: {
// case class SomeCaseClass extends scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val i: Int = _;
// <caseaccessor> <paramaccessor> val b: Byte = _;
// <caseaccessor> <paramaccessor> val c: Char = _;
// def <init>(i: Int, b: Byte, c: Char) = {
// super.<init>();
// ()
// }
// };
// object SomeCaseClass extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def i(): String = "Int";
// def b(): String = "Byte";
// def c(): String = "Char"
// };
// ()
//}