我已经定义了以下java Annotation
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
}
我定义了以下scala案例类:
@Prefer("xyz")
case class TestAnno(arg1 : String, @Prefer("abc") agr2 : String)
我能够使用反射看到在案例类级别定义的注释,但我无法访问为案例类arg2
的{{1}}成员定义的注释。反编译代码我发现变量声明或它的scala访问器似乎都没有注释。只有构造函数定义和TestAnno
方法似乎保留了case类声明中定义的参数的注释。
是否有其他方法可以强制scala编译器为case类中声明的字段生成注释,或者我必须读取构造函数并使用ASM ByteCode Library或ParaNamer等库来查找参数有哪些注释?需要一个主要适用于Scala案例类的解决方案。
答案 0 :(得分:15)
您只需要执行以下操作:
case class TestAnno(arg1 : String, @(Prefer @field)("abc") agr2 : String)
此处有更多信息http://www.scala-lang.org/api/current/#scala.annotation.meta.package
答案 1 :(得分:5)
Quentin的解决方案有效,但恕我直言,这对用户来说太过分析。
您可以使用标准反射API读取构造函数参数的注释。我需要这个用于宏实现。
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.annotation.StaticAnnotation
final class min(i: Long) extends StaticAnnotation
case class Foo(@min(1) c: String)
import scala.reflect.runtime.universe._
symbolOf[Foo].asClass.primaryConstructor.typeSignature.paramLists.head.head.annotations
// Exiting paste mode, now interpreting.
import scala.annotation.StaticAnnotation
defined class min
defined class Foo
import scala.reflect.runtime.universe._
res0: List[reflect.runtime.universe.Annotation] = List(min(1L))
答案 2 :(得分:2)
我做了一些搜索,并提供了两个解决方案。评论,建议,改进是受欢迎的,我已将此答案标记为维基。第一个基于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 answer:
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
}