如何从 AnyRef 对象获取反映的运行时方法?

Posted

技术标签:

【中文标题】如何从 AnyRef 对象获取反映的运行时方法?【英文标题】:How to get reflected runtime Method from AnyRef object? 【发布时间】:2018-08-02 15:19:56 【问题描述】:

我正在尝试在实例中获取反射的运行时方法,但它未显示在 decls 结果中:

val foo: AnyRef = new Object 
  def bar = 1

typeOf[foo.type].decls //Does not contain bar method

我尝试使用 Java 反射类并且它可以工作:

foo.getClass.getDeclaredMethods //It contains bar method

但我更喜欢使用 MethodSymbols 和 Scala 类型而不是 Java 类和方法反射。如何获取反射的 MethodSymbol?


我想要一个方法来查找作为 AnyRef 传递给方法栏的对象并调用它。如下所示:

def getBarMethodFromObj(obj: AnyRef): MethodSymbol = 
  //typeOf(obj).decl(TermName("bar")) this doesn't work

我不能使用 trait,因为 bar 可以有不同的参数并返回类型和数字。由于 Scala 不支持可变泛型参数,我计划使用反射来查找方法和调用,但这在 Scala 中也无法完成。我目前正在使用Java解决方案:

val bar = foo.getClass.getDeclaredMethods.find(_.getName == "bar")
bar.invoke(foo, params: _*)

但是 Java 反射不保留泛型类型,因为它会给 List 和 Map 等带来问题。所以我想知道我是否可以在 Scala 中实现它,或者是否有任何即将到来的解决方案

【问题讨论】:

请注意foo.bar 也不编译。值foo 的类型为foo.type,是AnyRef 的子类型,而AnyRef 没有方法bartypeOf[foo.type] gizmo 只包含在编译时可以推断的信息,而在编译时,从编译器的角度来看,没有任何东西表明foo 有一个方法bar @AndreyTyukin 感谢您的评论。我改成运行时反射方法 TypeTags 对于大多数用例来说已经足够了,我想,但你的用例是什么? 【参考方案1】:

我不知道您要做什么,但是删除 AnyRef 注释可以使您的代码正常工作:

val foo = new  def bar = 1 
typeOf[foo.type].decls // Contains bar method

如果需要类型注解(例如,在方法签名中),可以使用编译器推断的相同结构类型:

val foo:  def bar: Int  = new  def bar = 1 

如果您想从另一个方法中获取完整的方法列表,而不知道确切的类型,除了通过泛型,您可能对TypeTag 感兴趣:

import scala.reflect.runtime.universe. TypeTag, typeTag 
val foo = new  def bar = 1 
def getMethods[T: TypeTag](t: T) = typeTag[T].tpe.decls
getMethods(foo) // Contains bar

如果您不能使用TypeTag(可能是因为您无法更改 API),那么您最好使用 Java 反射 API。 Scala 反射 API 一般设计为使用类型信息,所以如果你只知道类型是AnyRef,它可能对你不起作用。


响应您的编辑:

我不能使用 trait,因为 bar 可以有不同的参数并返回类型和数字。

当然可以:

trait Foo[A, B] 
  def bar(a: A): B

如果您需要多个参数,只需让bar 取一个元组即可。如果你需要做一些元组不支持的列表操作,你可以考虑学习 HLists 和shapeless。

但是 Java 反射不保留泛型类型...

好吧,没有任何仅运行时的反射 API 可以帮助您。根本无法找出AnyRef 在运行时具有哪些通用参数,因为JVM 上不存在该信息。使用 TypeTags,或以上述方式使用trait

【讨论】:

在您的示例 3 中,foo 不是 AnyRef,而是声明了 bar 方法的匿名类,因此您可以使用 decls。在我的用例中, foo 是从一个没有已知类型但 AnyRef 的参数传递的,所以 decls 不起作用 @texasbruce 我想通了。我建议更改您的 API 以使用 TypeTags 或结构类型。否则,我认为您会被 Java 反射 API 卡住。 @texasbruce 我已更新我的答案以回应您的编辑。 它实际上并不适合我的需要,因为我想要的方法具有未定义数量的参数(可变参数),但 Scala 不支持泛型 var args 所以我想求助于使用反射。但是当参数类似于 List[Foo] 时,我还需要保留泛型参数。我想这对于 Scala 当前版本来说是不可能完成的任务。我接受了这个答案,因为它提到这是不可能的。

以上是关于如何从 AnyRef 对象获取反映的运行时方法?的主要内容,如果未能解决你的问题,请参考以下文章

如何反映动态对象的成员?

AnyVal与AnyRef

c++ 如何在运行时获取 COM 对象 Coclass 的 progid

反射机制

注册 UDF 时出现 Spark 错误:不支持 AnyRef 类型的架构

如何在运行时使用 jquery 创建 json 对象数组?