【问题标题】:How to invoke method on companion object via reflection?如何通过反射调用伴生对象上的方法?
【发布时间】:2016-08-25 00:09:38
【问题描述】:

我有一个类名和一个我知道其类型参数的方法,我想通过反射调用这个方法。

在java中我会做类似的事情:

Class.forName("foo").getMethod("name", ... type args...).invoke(null, ..args)

但是在 Scala 中,每当我尝试调用时,都会收到 null ref 错误。

我正在使用 scala 2.10.4

-- 编辑

我试过了:

$ scala
Welcome to Scala version 2.10.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_102).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class Foo { def bar(x: Int) = x }
defined class Foo

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> val foo = new Foo
foo: Foo = Foo@2db7a79b

scala> runtimeMirror(getClass.getClassLoader).reflect(foo)
res0: reflect.runtime.universe.InstanceMirror = instance mirror for Foo@2db7a79b

scala> res0.symbol.typeSignature.member(newTermName("bar"))
res1: reflect.runtime.universe.Symbol = method bar

scala> res0.reflectMethod(res1.asMethodSymbol)(42)
<console>:15: error: value asMethodSymbol is not a member of reflect.runtime.universe.Symbol
              res0.reflectMethod(res1.asMethodSymbol)(42)
                                      ^

作为"Dynamic" method invocation with new Scala reflection API 的示例,它似乎不再起作用了

这是我正在尝试做的测试:

object Test{
  def run(): Boolean = true
}

class InvokeTests extends FlatSpec with Matchers {
  "invoke" should "do" in {
    import scala.reflect.runtime.universe._

    val mirror = runtimeMirror(this.getClass.getClassLoader)

    val moduleSymbol = mirror.moduleSymbol(Class.forName(Test.getClass.getName))

    val reflected = mirror.reflect(moduleSymbol)

    val methodName = reflected.symbol.typeSignature.member(newTermName("run"))

    reflected.reflectMethod(methodName.asMethod)() shouldBe true
  }
}

【问题讨论】:

  • 这里有一个答案:stackoverflow.com/questions/11062166/… 在答案中有一个链接也可以通过反射 api 到达伴随对象。
  • 嗯,@Samar 这个例子似乎不起作用(也许自 scala 版本以来发生了变化?)
  • 请检查答案。它适用于 Scala 2.11.8

标签: scala


【解决方案1】:

参考scala doc reflection overview

// get runtime universe
val ru = scala.reflect.runtime.universe

// get runtime mirror
val rm = ru.runtimeMirror(getClass.getClassLoader)

// define class and companion object
class Boo{def hey(x: Int) = x}; object Boo{def hi(x: Int) = x*x}

// get instance mirror for companion object Boo
val instanceMirror = rm.reflect(Boo)

// get method symbol for the "hi" method in companion object
val methodSymbolHi = ru.typeOf[Boo.type].decl(ru.TermName("hi")).asMethod

// get method mirror for "hi" method
val methodHi = instanceMirror.reflectMethod(methodSymbolHi)

// invoke the method "hi"
methodHi(4)  // 16

【讨论】:

    【解决方案2】:

    下面的 Scala 2.13.3 示例编码在两种不同的情况下调用通用方法 apply,通过查找引用相应示例类的伴随对象:

    package spielwiese.reflection_versuche
    
    object Structural_Typing_Companion_Object_Invoke_Testframe {
    
      // Example 1
      case class Person01(vorname: String, name: String)
    
      object Person01 {
        def apply(x: Integer): Person01 = new Person01(s"Prename_$x", s"Lastname_$x")
      }
    
      // Example 2
      case class Person02(vorname: String, name: String)
    
      object Person02 {
        def apply(x: Integer): Person02 = new Person02(s"Prename_$x", s"Lastname_$x")
      }
    
      // Invocation demo:
    
      import scala.reflect.runtime.{universe => ru}
    
      def callGenericCompanionObjectMethod[T](implicit typetag: ru.TypeTag[T]) = {
    
        val m = ru.runtimeMirror(getClass.getClassLoader)
    
        val o1 = m.reflectModule(ru.typeOf[T].typeSymbol.companion.asModule).instance
    
        val o2 = o1.asInstanceOf[{ def apply(x: Integer): T }] // "structural typing"
    
        o2.apply(123)
    
      }
    
      def main(args: Array[String]): Unit = {
    
        println(callGenericCompanionObjectMethod[Person01]) 
        // prints: "Person01(Prename_123,Lastname_123)"
    
        println(callGenericCompanionObjectMethod[Person02]) 
        // prints: "Person02(Prename_123,Lastname_123)"
    
      }
    
    }
    

    【讨论】:

      【解决方案3】:

      如果您只有TypeFoo(问题是foo):

      % scala
      Welcome to Scala 2.13.1 (OpenJDK 64-Bit Server VM, Java 15-loom).
      Type in expressions for evaluation. Or try :help.
      
      scala> :paste
      // Entering paste mode (ctrl-D to finish)
      
      import scala.reflect.runtime.currentMirror
      import scala.reflect.runtime.universe._
      import scala.reflect.runtime.universe.definitions._
      
      def f(tpe: Type): Unit = {
        val m = currentMirror
        val im = m.reflect(
          m.reflectModule(tpe.typeSymbol.asClass.companion.asModule).instance
        )
        val method = tpe.companion.decl(TermName("name")).asMethod
        val v = im.reflectMethod(method)("John", "Doe")
        println(v)
      }
      
      // Exiting paste mode, now interpreting.
      
      import scala.reflect.runtime.currentMirror
      import scala.reflect.runtime.universe._
      import scala.reflect.runtime.universe.definitions._
      f: (tpe: reflect.runtime.universe.Type)Unit
      
      scala> :paste
      // Entering paste mode (ctrl-D to finish)
      
      case class Foo()
      case object Foo { def name(fn: String, ln: String): String = s"$fn $ln" }
      
      // Exiting paste mode, now interpreting.
      
      defined class Foo
      defined object Foo
      
      scala> f(typeOf[Foo])
      John Doe
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-15
        • 2020-01-18
        • 2023-04-05
        • 2012-09-22
        相关资源
        最近更新 更多