我将发布一个利用 scala 宏系统的解决方案(旧类型,而不是 Scala 3.0 引入的最新版本)。这对你来说可能有点矫枉过正......
顺便说一句,如果您只想访问父值(例如获取键、值对),您可以:
- 给定一个类型标签,获取所有父母;
- 从中提取所有访问器(vals);
- 对于每个 val,获取其值;
- 最后返回一个包含所有访问器的列表
所以,我尝试逐步解决每个问题。
首先,我们要把宏定义写成:
object Macros {
def accessors[T](element : T): String = macro MacrosImpl.accessors[T]
}
object MacrosImpl {
def accessors[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[String] = ...
}
对于第一点,我们可以使用 c.universe 来利用反射宏编程 API:
import c.universe._
val weakType = weakTypeTag[T] //thanks to the WeakTypeTag typeclass
val parents = weakType.tpe.baseClasses
对于第二点,我们可以遍历父类,然后只获取公共访问器:
val accessors = parents
.map(weakType.tpe.baseType(_))
.flatMap(_.members)
.filter(_.isPublic)
.filter(_.isMethod)
.map(_.asMethod)
.filter(_.isAccessor)
.toSet
因此,例如,如果我们写 Macros.accessors[Details](person),accessors 将产生 age 和 address。
为了获取价值,我们可以利用 quasiqouting。所以,首先我们只取值名称:
val names = accessors
.map(_.fullName)
.map(_.split("\\."))
.map(_.reverse.head)
然后我们将它们转换成TermName:
val terms = names.map(TermName(_))
最后,我们将每个术语转换为包含 val 名称及其值的键值元组:
val accessorValues = terms
.map(name => c.Expr[(String, Any)](q"(${name.toString}, ${element}.${name})"))
.toSeq
最后一步是将Seq[Expr[(String, Any)] 转换为Expr[Seq[(String, Any)]。一种方法是利用递归、reify 和 splicing 表达式:
def seqToExprs(seq: Seq[Expr[(String, Any)]]): c.Expr[Seq[(String, Any)]] =
seq.headOption match {
case Some(head) =>
c.universe.reify(
Seq((head.splice._1, head.splice._2)) ++
seqToExprs(seq.tail).splice
)
case _ => c.Expr[Seq[(String, Any)]](q"Seq.empty")
}
所以现在我决定返回一个字符串表示(但你可以随意操作它):
val elements = seqToExprs(accessorValues)
c.Expr[String](q"${elements}.mkString")
您可以将其用作:
import Macros._
class A(val a : Int)
class B(val b : Int) extends A(b)
class C(val c: Int) extends B(c)
//println(typeToString[List[Set[List[Double]]]])
val c = new C(10)
println(accessors[C](c)) // prints (a, 10)(b, 10)(c, 10)
println(accessors[B](c)) // prints (a, 10)(b, 10)
println(accessors[A](c)) // prints (a, 10)
并且,使用您的示例:
// Your example:
case class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address)
class Details(val age: Int, val address: String)
val person = Person("Alex", 33, "Europe")
println(accessors[Details](person)) // prints (address,Europe)(age,33)
println(accessors[Person](person)) // prints (address,Europe)(age,33)(name,Alex)
Here 有一个实现了宏的存储库。
Scala 3.0 引入了一个更安全、更干净的宏系统,如果你使用它并想更进一步,可以阅读这些文章: