【问题标题】:Troubles with getting case class fields via reflection通过反射获取案例类字段的问题
【发布时间】:2021-01-26 13:17:48
【问题描述】:

我有一个 scala 代码:特征模型及其实现案例类 Category 带有我希望稍后阅读的自定义注释

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 {

}

我有 trait 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 参数传递的案例类的字段,所以我调用了两个函数

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
  }

但它们都不起作用

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

【问题讨论】:

  • 可以在编译时找到注解shapeless.Annotations[ExtraFields, MyCategory].apply()。这会产生None :: None :: None :: Some(ExtraFields@4f5991f6) :: HNil
  • @DmytroMitin,这对我来说更方便,谢谢

标签: scala generics reflection scala-reflect


【解决方案1】:

getClassFields 缺少类型类 WeakTypeTag

此外,您没有在 trait DBModel[T &lt;: Model] 中传递类型类。

一种解决方案是将它们定义为T 类型的上下文边界。但要使其工作,需要通过构造函数传递这些类型类。而且由于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

【讨论】:

  • 能否请您解释一下`@(ExtraFields @param @getter) parent: Option[MyCategory] ​​= None`` 或提供有关@param 和@getter 的解释链接。在操场上,这条线抛出错误scastie.scala-lang.org/kJ8rlP98SaOBSzA5Ap4oIA
  • @DmitryReutov 请阅读mets docs
  • @DmitryReutov 您缺少导入 import scala.annotation.meta.{getter, param}
  • @IvanStanislavovich,好的!我得到了
猜你喜欢
  • 2020-07-10
  • 1970-01-01
  • 2013-04-11
  • 1970-01-01
  • 2013-08-15
  • 1970-01-01
  • 2011-08-20
  • 2013-10-24
  • 1970-01-01
相关资源
最近更新 更多