2012-07-13 28 views
9

Tôi đã chú thích java sau định nghĩaLàm thế nào để truy cập Chú định nghĩa trên sân lớp trường hợp tại Runtime

@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.FIELD}) 
    @Retention(RetentionPolicy.RUNTIME) 
    public @interface MyAnnotation { 
    String value() default ""; 
    } 

Và tôi đã lớp trường hợp scala sau định nghĩa:

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

Tôi có thể thấy chú thích được xác định ở cấp độ trường hợp sử dụng sự phản chiếu nhưng tôi không thể truy cập chú thích được xác định cho arg2 thành viên của trường hợp lớp TestAnno. De-biên dịch mã Tôi thấy rằng không phải là tuyên bố biến hoặc scala accessor của nó dường như có chú thích. Chỉ định nghĩa hàm tạo và phương thức copy dường như giữ lại chú thích cho các tham số như được định nghĩa trong khai báo lớp. Có một số cách khác để buộc trình biên dịch scala tạo chú thích cho các trường được khai báo trong trường hợp lớp hay tôi phải đọc hàm tạo và sử dụng thư viện như ASM ByteCode Library hoặc ParaNamer để tìm tham số nào có chú thích nào? Cần một giải pháp mà sẽ làm việc chủ yếu cho các trường hợp Scala.

Trả lời

2

Tôi đã thực hiện một số tìm kiếm xung quanh và đã đi kèm với hai giải pháp. Nhận xét, đề xuất, cải tiến được hoan nghênh, tôi đã đánh dấu câu trả lời này là wiki. Mục đầu tiên dựa trên 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 
    } 

Cách tiếp cận thứ hai sử dụng ScalaSignatures và được dựa trên này 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 
    } 
9

Bạn chỉ cần làm như sau:

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

Thông tin thêm ở đây http://www.scala-lang.org/api/current/#scala.annotation.meta.package

+0

Tùy thuộc vào cách bạn truy cập các tham số này bằng cách phản ánh, bạn có thể phải thêm '@ setter',' @ getter' hoặc '@ param' thay thế hoặc cùng với' @ field'. (Bạn có thể thử với tất cả chúng, và loại bỏ chúng từng người một.) – al3xar

5

Quentin của giải pháp làm việc, nhưng IMHO nó quá nhiều boilerplate cho người dùng.

Bạn có thể đọc chú thích trên các đối số hàm tạo với API phản chiếu tiêu chuẩn. Tôi cần điều này để thực hiện macro.

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)) 
+0

Thông tin chú thích có thể được truy cập như thế nào? – bashan

Các vấn đề liên quan