通过反射获取 case class 字段的问题
Troubles with getting case class fields via reflection
我有一个scala代码:特征模型及其实现案例class带有自定义注释的类别,我希望稍后阅读
import scala.annotation.StaticAnnotation
class ExtraFields() extends StaticAnnotation
trait Model {}
case class MyCategory( id: Option[Int],
name: String,
level: Option[Int],
@ExtraFields
parent: Option[MyCategory] = None
) extends Model {
}
我有特征 DBModel 及其实现 MyCategoryModel。我将 MyCategory 作为类型参数传递给 DBModel
trait DBModel[T <: Model] {
lazy val fields = getClassFields[T]
lazy val fields2 = getClassFields2[T]
}
object MyCategoryModel extends DBModel[MyCategory] {
def getFields = fields
def getFields2 = fields2
}
我希望 trait DBModel 读取作为 T 参数传递的 case class 的字段,所以我调用了两个函数
def getClassFields[T] = {
symbolOf[T].asClass.primaryConstructor
.typeSignature.paramLists.head.map {v =>
v.name.toString -> v.annotations
}
}
def getClassFields2[T: TypeTag]: Iterable[(String, List[universe.Annotation])] =
typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor =>
m.name.toString -> m.annotations
}
但其中 none 正在工作
val res = MyCategoryModel.getFields // runtime exception: free type T is not a class
val res2 = MyCategoryModel.getFields2 // compile error: No TypeTag available for T
如果我直接打电话给他们
getClassFields[MyCategory]
getClassFields2[MyCategory]
对于第一个函数,我得到了同样的错误,对于第二个函数,我得到了结果,但是 universe.annotations 看不到我的 ExtraFields 注释,为每个字段返回空列表
你能解释一下这个黑魔法以及如何在我的情况下征服它吗
谢谢
UPD 游乐场https://scastie.scala-lang.org/DTTTyA0CSTSZ0Vj7KFW6Hw
您缺少类型 class WeakTypeTag
getClassFields
。
此外,您没有在特征 DBModel[T <: Model]
.
中传递类型 classes
一种解决方案是将它们定义为 T
类型的上下文边界。但要使其工作,需要通过构造函数传递这些类型 classes。而且因为 traits 没有构造函数,所以解决方案是使用 abstract class
.
所以下面修复了编译问题
abstract class DBModel[T <: Model : WeakTypeTag : TypeTag] {
lazy val fields = getClassFields[T]
lazy val fields2 = getClassFields2[T]
}
def getClassFields[T: WeakTypeTag] = ???
def getClassFields2[T: TypeTag] = ???
由于指定注释的方式,方法 getClassFields2
未找到注释。如果定义如下,两种方法都有效。有关详细信息,请阅读 meta docs
import scala.annotation.meta.{getter, param}
case class MyCategory(
id: Option[Int],
name: String,
level: Option[Int],
@(ExtraFields @param @getter) parent: Option[MyCategory] = None
) extends Model
我有一个scala代码:特征模型及其实现案例class带有自定义注释的类别,我希望稍后阅读
import scala.annotation.StaticAnnotation
class ExtraFields() extends StaticAnnotation
trait Model {}
case class MyCategory( id: Option[Int],
name: String,
level: Option[Int],
@ExtraFields
parent: Option[MyCategory] = None
) extends Model {
}
我有特征 DBModel 及其实现 MyCategoryModel。我将 MyCategory 作为类型参数传递给 DBModel
trait DBModel[T <: Model] {
lazy val fields = getClassFields[T]
lazy val fields2 = getClassFields2[T]
}
object MyCategoryModel extends DBModel[MyCategory] {
def getFields = fields
def getFields2 = fields2
}
我希望 trait DBModel 读取作为 T 参数传递的 case class 的字段,所以我调用了两个函数
def getClassFields[T] = {
symbolOf[T].asClass.primaryConstructor
.typeSignature.paramLists.head.map {v =>
v.name.toString -> v.annotations
}
}
def getClassFields2[T: TypeTag]: Iterable[(String, List[universe.Annotation])] =
typeOf[T].members.collect {
case m: MethodSymbol if m.isCaseAccessor =>
m.name.toString -> m.annotations
}
但其中 none 正在工作
val res = MyCategoryModel.getFields // runtime exception: free type T is not a class
val res2 = MyCategoryModel.getFields2 // compile error: No TypeTag available for T
如果我直接打电话给他们
getClassFields[MyCategory]
getClassFields2[MyCategory]
对于第一个函数,我得到了同样的错误,对于第二个函数,我得到了结果,但是 universe.annotations 看不到我的 ExtraFields 注释,为每个字段返回空列表
你能解释一下这个黑魔法以及如何在我的情况下征服它吗 谢谢
UPD 游乐场https://scastie.scala-lang.org/DTTTyA0CSTSZ0Vj7KFW6Hw
您缺少类型 class WeakTypeTag
getClassFields
。
此外,您没有在特征 DBModel[T <: Model]
.
一种解决方案是将它们定义为 T
类型的上下文边界。但要使其工作,需要通过构造函数传递这些类型 classes。而且因为 traits 没有构造函数,所以解决方案是使用 abstract class
.
所以下面修复了编译问题
abstract class DBModel[T <: Model : WeakTypeTag : TypeTag] {
lazy val fields = getClassFields[T]
lazy val fields2 = getClassFields2[T]
}
def getClassFields[T: WeakTypeTag] = ???
def getClassFields2[T: TypeTag] = ???
由于指定注释的方式,方法 getClassFields2
未找到注释。如果定义如下,两种方法都有效。有关详细信息,请阅读 meta docs
import scala.annotation.meta.{getter, param}
case class MyCategory(
id: Option[Int],
name: String,
level: Option[Int],
@(ExtraFields @param @getter) parent: Option[MyCategory] = None
) extends Model