12

次のJavaアノテーションを定義しました

   @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リフレクションを使用してケースクラスレベルで定義されたアノテーションを表示できますが、ケースクラスのメンバーに定義されたアノテーションにアクセスできませんTestAnno。コードのコンパイルを解除する変数宣言もそのscalaアクセサーもアノテーションを持っていないようです。コンストラクターの定義とcopyメソッドのみが、caseクラス宣言で定義されているパラメーターのアノテーションを保持しているようです。

ケースクラスで宣言されたフィールドのアノテーションをscalaコンパイラに強制的に生成させる他の方法はありますか、またはコンストラクタを読み取り、ASM ByteCode LibraryParaNamerなどのライブラリを使用して、どのパラメータにどのアノテーションがあるかを見つける必要がありますか?主にScalaケースクラスで機能するソリューションが必要です。

4

3 に答える 3

17

次のことを行うだけです。

case class TestAnno(arg1 : String, @(Prefer @field)("abc") agr2 : String)

詳細はこちらhttp://www.scala-lang.org/api/current/#scala.annotation.meta.package

于 2012-11-04T21:48:56.710 に答える
2

私はいくつかの検索を行い、2つの解決策がありました。コメント、提案、改善を歓迎します。この回答を wiki としてマークしました。最初のものはScalaBeansParaNamerに基づいています。

  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
  }

2 番目のアプローチは 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
  }
于 2012-07-14T04:29:50.160 に答える