从Java方法获取Scala批注

问题描述

我有一个Java Method实例,该实例代表具有Scala注释(扩展了StaticAnnotation)的Scala函数。我知道我可以使用Method.getAnnotation(classOf[SomeJavaAnnotation])获取Java注释,但这将为Scala注释返回null

如何从中获取Scala注释?看来我需要先将其转换为Scala MethodSymbol,但是我没有看到一种明显的方法来做到这一点,而且我只看到了展示如何进行反方向转换的资源(来自Scala {{1} }到Java MethodSymbol)。

解决方法

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