我做了一些搜索,并提出了两种解决方案。欢迎发表评论、建议和改进,我已将此答案标记为 wiki。第一个基于ScalaBeans和ParaNamer。
def valNamesWithAnnotations[T <: AnyRef](obj : T)(implicit m : Manifest[T]) : List[(String, List[java.lang.annotation.Annotation])] = {
val descriptor = descriptorOf(obj.getClass)
val c = descriptor.beanType.erasure
val constructor: Option[Constructor[_]] = {
if (c.getConstructors().isEmpty) None
else Some(c.getConstructors()(0).asInstanceOf[Constructor[_]])
}
val paranamer = new BytecodeReadingParanamer
val ctorParameterNames = constructor.map(paranamer.lookupParameterNames(_)).getOrElse(scala.Array[String]()).toList
val ctorParamAnnos = constructor.getOrElse(sys.error("Cannot find constructor entry for class " + c.getName)).getParameterAnnotations
val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])]
val paramIter = ctorParameterNames.iterator
val annoIter = ctorParamAnnos.iterator
while(paramIter.hasNext && annoIter.hasNext ) {
builder += ((paramIter.next, annoIter.next.toList))
}
builder.result
}
第二种方法使用 ScalaSignatures 并基于此SO 答案:
def valNamesWithAnnotations[C: ClassManifest] : List[(String, List[java.lang.annotation.Annotation])] = {
val cls = classManifest[C].erasure
val ctors = cls.getConstructors
assert(ctors.size == 1, "Class " + cls.getName + " should have only one constructor")
val sig = ScalaSigParser.parse(cls).getOrElse(sys.error("No ScalaSig for class " + cls.getName + ", make sure it is a top-level case class"))
val classSymbol = sig.parseEntry(0).asInstanceOf[ClassSymbol]
assert(classSymbol.isCase, "Class " + cls.getName + " is not a case class")
val tableSize = sig.table.size
val ctorIndex = (1 until tableSize).find { i =>
sig.parseEntry(i) match {
case m @ MethodSymbol(SymbolInfo("<init>", owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == 0 => true
case _ => false
}
case _ => false
}
}.getOrElse(sys.error("Cannot find constructor entry in ScalaSig for class " + cls.getName))
val paramsListBuilder = List.newBuilder[String]
for (i <- (ctorIndex + 1) until tableSize) {
sig.parseEntry(i) match {
case MethodSymbol(SymbolInfo(name, owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == ctorIndex => paramsListBuilder += name
case _ =>
}
case _ =>
}
}
val paramAnnoArr = ctors(0).getParameterAnnotations
val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])]
val paramIter = paramsListBuilder.result.iterator
val annoIter = paramAnnoArr.iterator
while(paramIter.hasNext && annoIter.hasNext ) {
builder += ((paramIter.next, annoIter.next.toList))
}
builder.result
}