【问题标题】:Is there any Scala feature that allows you to call a method whose name is stored in a string?是否有任何 Scala 功能允许您调用名称存储在字符串中的方法?
【发布时间】:2011-01-04 20:20:22
【问题描述】:

假设您有一个包含方法名称的字符串、一个支持该方法的对象和一些参数,是否有一些语言特性允许您动态调用它?

有点像 Ruby 的 send 参数。

【问题讨论】:

    标签: scala dynamic language-features


    【解决方案1】:

    您可以通过 Java 中的反射来做到这一点:

    class A {
      def cat(s1: String, s2: String) = s1 + " " + s2
    }
    val a = new A
    val hi = "Hello"
    val all = "World"
    val method = a.getClass.getMethod("cat",hi.getClass,all.getClass)
    method.invoke(a,hi,all)
    

    如果您希望在 Scala 中更简单,您可以创建一个为您执行此操作的类,以及一个隐式转换:

    case class Caller[T>:Null<:AnyRef](klass:T) {
      def call(methodName:String,args:AnyRef*):AnyRef = {
        def argtypes = args.map(_.getClass)
        def method = klass.getClass.getMethod(methodName, argtypes: _*)
        method.invoke(klass,args: _*)
      }
    }
    implicit def anyref2callable[T>:Null<:AnyRef](klass:T):Caller[T] = new Caller(klass)
    a call ("cat","Hi","there")
    

    但是,这样做会将编译时错误转换为运行时错误(即它本质上绕过了类型系统),因此请谨慎使用。

    (编辑:并在上面的链接中查看 NameTransformer 的使用——如果您尝试使用运算符,添加这将有所帮助。)

    【讨论】:

    • 我收到以下错误。 List(1, 2) call ("take", 1)。错误是type mismatch; found : Int(1) required: AnyRef Note: an implicit exists from scala.Int =&gt; java.lang.Integer, but methods inherited from Object are rendered ambiguous. This is to avoid a blanket implicit which would convert any scala.Int to any AnyRef. You may wish to use a type ascription: x: java.lang.Integer. 有没有办法处理原始类型? (我想动态生成参数列表...可能是Any的列表)
    • @dips - 您可以在途中使用.asInstanceOf[AnyRef](也可以与Any 一起使用),或者错误提示的1: java.lang.Integer。或者,如果你真的需要,你可以匹配原语并逐个处理,尽管你不应该在这里。
    • 谢谢!所以我的call 函数签名现在采用args: Any* 并将其转换为call 的主体中的AnyRef* 作为args map {_.asInstanceOf[AnyRef]}。希望这很好。不知道其中的微妙之处,所以问。
    • 此解决方案不适用于列表。该列表作为scala.collection.immutable.$colon$colon 传递给方法,而不是List[Type]。有什么想法吗?
    • @ArthurC - 您可以像编译器一样手动编码名称(请参阅codecommit.com/blog/java/interop-between-java-and-scala 获取翻译表),或者我认为有一个外部可调用的编译器方法可以做到这一点我忘记了谁的名字。
    【解决方案2】:

    是的。这叫做反射。 Here's a link to one way, using some experimental stuff 但是您应该记住,Scala 不是一种动态语言,并且可能无法轻松完成脚本语言可以完成的一些事情。您最好对字符串进行匹配,然后调用正确的方法。

    【讨论】:

      【解决方案3】:

      是的,你可以! 您将需要方法对象的.invoke() 方法。下面的简单例子:

       import scala.util.Try
      
       case class MyCaseClass(i: String) {
       def sayHi = {
           println(i)
         }
       }
       val hiObj = MyCaseClass("hi")
       val mtdName = "sayHi"
       // Method itself as an object
       val mtd = hiObj.getClass.getMethod(mtdName)
       Try {mtd.invoke(hiObj)}.recover { case _ => ()}
      

      在此处查看代码:https://scastie.scala-lang.org/vasily802/WRsRpgSoSayhHBeAvogieg/9

      【讨论】:

        【解决方案4】:
        scala> val commandExecutor = Map("cleanup" -> {()=> println("cleanup successfully")} )
        commandExecutor: scala.collection.immutable.Map[String,() => Unit] = Map(cleanup -> <function0>)
        
        scala> val command="cleanup"
        command: String = cleanup
        
        scala> commandExecutor(command).apply
        cleanup successfully
        

        【讨论】:

        • 非常干净的反射替代方案
        猜你喜欢
        • 2012-06-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-20
        • 2016-12-08
        • 1970-01-01
        • 2021-01-10
        相关资源
        最近更新 更多