从 Java 方法获取 Scala 注释

Get Scala annotation from Java Method

我有一个 Java Method 的实例,它表示带有 Scala 注释的 Scala 函数(扩展 StaticAnnotation)。我知道我可以使用 Method.getAnnotation(classOf[SomeJavaAnnotation]) 来获得 Java 注释,但是这个 returns null 用于 Scala 注释。

如何从中获取 Scala 注释?看来我需要先将它转换为 Scala MethodSymbol,但我没有看到一个明显的方法来做到这一点,我只看到资源显示如何走另一条路(来自 Scala MethodSymbol 到 Java Method).

Scala 注释对于 Java 反射是不可见的。

两者都

  • 使用 Java 注释(具有运行时保留策略),然后它们将对 Java 反射可见,或者

  • 使用Scala reflection,可以看到Scala注解


这就是 java.lang.reflect.Method 可以转换成 scala.reflect.runtime.universe.MethodSymbol

的方法
import java.lang.reflect.Method
import scala.reflect.runtime
import scala.reflect.runtime.universe._

def methodToMethodSymbol(method: Method): MethodSymbol = {
  val runtimeMirror = runtime.currentMirror
  val classSymbol = runtimeMirror.classSymbol(method.getDeclaringClass)
  classSymbol.typeSignature.decl(TermName(method.getName)).asMethod // (*)
}

如果该方法有重载版本,您将不得不替换 行 (*) 与

classSymbol.typeSignature.decl(TermName(method.getName)).alternatives.find(
  _.asMethod.paramLists.flatten.map(_.typeSignature.erasure.typeSymbol.asClass) == 
    method.getParameterTypes.map(runtimeMirror.classSymbol).toList
).get.asMethod

另一个实现:

def methodToMethodSymbol(method: Method): MethodSymbol = {
  val runtimeMirror = runtime.currentMirror
  val castedRuntimeMirror = runtimeMirror.asInstanceOf[{
    def methodToScala(meth: Method): scala.reflect.internal.Symbols#MethodSymbol
  }]

  castedRuntimeMirror.methodToScala(method).asInstanceOf[MethodSymbol]
}

测试:

class myAnnotation extends StaticAnnotation

class MyClass {
  @myAnnotation
  def myMethod(i: Int): Unit = ()
}

val clazz = classOf[MyClass]
val method = clazz.getMethod("myMethod", classOf[Int])
  
val methodSymbol = methodToMethodSymbol(method) // method myMethod
methodSymbol.annotations // List(myAnnotation)

为了以防万一,这里把scala.reflect.runtime.universe.MethodSymbol反向转换成java.lang.reflect.Method

def methodSymbolToMethod(methodSymbol: MethodSymbol): Method = {
  val runtimeMirror = runtime.currentMirror
  val classSymbol = methodSymbol.owner.asClass
  val clazz = runtimeMirror.runtimeClass(classSymbol)
  val paramClasses = methodSymbol.paramLists.flatten.map(paramSymbol =>
    runtimeMirror.runtimeClass(paramSymbol.typeSignature.erasure)
  )
  clazz.getMethod(methodSymbol.name.toString, paramClasses: _*)
}

另一个实现:

def methodSymbolToMethod(methodSymbol: MethodSymbol): Method = {
  type InternalMethodSymbol = scala.reflect.internal.Symbols#MethodSymbol
  val runtimeMirror = runtime.currentMirror
  val castedRuntimeMirror = runtimeMirror.asInstanceOf[{
    def methodToJava(sym: InternalMethodSymbol): Method
  }]

  castedRuntimeMirror.methodToJava(
    methodSymbol.asInstanceOf[InternalMethodSymbol]
  )
}

Get a java.lang.reflect.Method from a reflect.runtime.universe.MethodSymbol