从具有特定注释的 Scala 案例类中获取字段名称


final class ContactInfo extends StaticAnnotation{}
case class Person(val id: String,
              val firstName: String,
              val lastName: String,
              @ContactInfo val phoneNumbers: Seq[String],
              @ContactInfo val email: String)
def getContactInfoFields[T: TypeTag]: Seq[String] = {
???
}

预期输出 getContactInfoFields[Person] = ("电话号码", "电子邮件"(

我尝试过的 SO 上回答类似问题

def getContactInfoFields[T: TypeTag]: Seq[String] = {
  val fields = typeOf[T].members.collect{ case s: TermSymbol => s }.
    filter(s => s.isVal || s.isVar)
  fields.filter(_.annotations.exists(_.isInstanceOf[ContactInfo]))
    .map(x=>x.name.toString).toSeq
}

但是,在实践中,这会返回一个空序列。 我错过了什么?

您可以在类型级别表示此信息。

sealed trait ContactInfo
case class PhoneNumbers(numbers: Seq[String]) extends ContactInfo
case class Email(email: String) extends ContactInfo
case class Person(id: String, firstName: String, lastName: String, phoneNumbers: PhoneNumbers, email: Email)
def contactInfo[T: TypeTag] = typeOf[T].members.filter(!_.isMethod).map(_.typeSignature).collect {
  case t if t <:< typeOf[ContactInfo] => t.typeSymbol.name.toString
}

调用contactInfo[Person]返回Iterable[String] = List(Email, PhoneNumbers)

谢谢大家的帮助! 我已经设法提出了一个可行的解决方案。 事实证明,我试图将JavaUniverse.reflection.Type与Scala Universe反射类型进行比较,这就是filter语句失败的原因。 以下函数按预期返回

def listContactInfoFields(symbol: TypeSymbol): Seq[String] = {
  val terms = symbol.asClass.primaryConstructor.typeSignature.paramLists.head
  val annotatedFields = terms.filter(_.annotations.exists(_.tree.tpe =:= typeOf[ContactInfo]))
  annotatedFields.map(_.name.toString)
}

最新更新