仅获取超级 class 字段
Get only super class fields
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")
val details = person.asInstanceOf[Details] // ???
println(details) // I want only Details class fields
我有这 2 个 classes。实际上,两者都有很多领域。在某个地方,我只需要 superclass 的字段,取自 Person class.
有一个很好的方法可以只获取超 class 值而不是逐个字段映射它们吗?
*我很确定 json 写 class 细节会遇到一些问题(这不是 class 的情况,也没有一个单例对象,但这是另一个主题)
如果我答对了你的问题,那么你可能是在问我 java 的运行时多态性或动态方法分派。
如果是这样,您可能必须同时创建 class 而不是大小写 class
class Details( val age: Int, val address: String)
class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address) {
}
现在创建person对象并引用superclass(详情)
val detail:Details = new Person("Alex", 33, "Europe")
println(detail.address)
println(detail.age)
这样你就可以获得唯一的address
和age
另一种方法是,为什么我们不能创建 Details
一个单独的实体,例如:
case class Details( age: Int, address: String)
case class Person(name: String,
details: Details
)
val detail = Person("Alex", Details(10,"Europe") )
输出:
println(detail.details)
Details(10,Europe)
我将 post 一个利用 Scala 宏系统的解决方案(旧类型,不是 Scala 3.0 引入的最新版本)。这对你来说可能有点矫枉过正......
顺便说一句,如果你只想访问父值(例如获取键值对),你可以:
- 给定一个类型标签,得到所有父类;
- 从中提取所有访问器(vals);
- 对于每个 val,获取它的值;
- 最后 return 是一个包含所有访问器的列表
所以,我试着一步步解决每一个问题。
首先,我们要把宏定义写成:
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] = ...
}
对于第一点,我们可以利用反射宏编程 API 使用 c.universe
:
import c.universe._
val weakType = weakTypeTag[T] //thanks to the WeakTypeTag typeclass
val parents = weakType.tpe.baseClasses
对于第二点,我们可以遍历父 类 然后只采用 public 访问器:
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。所以,首先我们只取值 name:
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")
}
所以现在我决定 return 一个字符串表示(但你可以随意操作它):
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 引入了一个更安全更干净的宏系统,如果你使用它并且想更进一步可以阅读这些文章:
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")
val details = person.asInstanceOf[Details] // ???
println(details) // I want only Details class fields
我有这 2 个 classes。实际上,两者都有很多领域。在某个地方,我只需要 superclass 的字段,取自 Person class.
有一个很好的方法可以只获取超 class 值而不是逐个字段映射它们吗?
*我很确定 json 写 class 细节会遇到一些问题(这不是 class 的情况,也没有一个单例对象,但这是另一个主题)
如果我答对了你的问题,那么你可能是在问我 java 的运行时多态性或动态方法分派。 如果是这样,您可能必须同时创建 class 而不是大小写 class
class Details( val age: Int, val address: String)
class Person(name: String,
override val age: Int,
override val address: String
) extends Details(age, address) {
}
现在创建person对象并引用superclass(详情)
val detail:Details = new Person("Alex", 33, "Europe")
println(detail.address)
println(detail.age)
这样你就可以获得唯一的address
和age
另一种方法是,为什么我们不能创建 Details
一个单独的实体,例如:
case class Details( age: Int, address: String)
case class Person(name: String,
details: Details
)
val detail = Person("Alex", Details(10,"Europe") )
输出:
println(detail.details)
Details(10,Europe)
我将 post 一个利用 Scala 宏系统的解决方案(旧类型,不是 Scala 3.0 引入的最新版本)。这对你来说可能有点矫枉过正......
顺便说一句,如果你只想访问父值(例如获取键值对),你可以:
- 给定一个类型标签,得到所有父类;
- 从中提取所有访问器(vals);
- 对于每个 val,获取它的值;
- 最后 return 是一个包含所有访问器的列表
所以,我试着一步步解决每一个问题。 首先,我们要把宏定义写成:
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] = ...
}
对于第一点,我们可以利用反射宏编程 API 使用 c.universe
:
import c.universe._
val weakType = weakTypeTag[T] //thanks to the WeakTypeTag typeclass
val parents = weakType.tpe.baseClasses
对于第二点,我们可以遍历父 类 然后只采用 public 访问器:
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。所以,首先我们只取值 name:
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")
}
所以现在我决定 return 一个字符串表示(但你可以随意操作它):
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 引入了一个更安全更干净的宏系统,如果你使用它并且想更进一步可以阅读这些文章: