【发布时间】:2011-01-04 20:20:22
【问题描述】:
假设您有一个包含方法名称的字符串、一个支持该方法的对象和一些参数,是否有一些语言特性允许您动态调用它?
有点像 Ruby 的 send 参数。
【问题讨论】:
标签: scala dynamic language-features
假设您有一个包含方法名称的字符串、一个支持该方法的对象和一些参数,是否有一些语言特性允许您动态调用它?
有点像 Ruby 的 send 参数。
【问题讨论】:
标签: scala dynamic language-features
您可以通过 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 => 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的列表)
.asInstanceOf[AnyRef](也可以与Any 一起使用),或者错误提示的1: java.lang.Integer。或者,如果你真的需要,你可以匹配原语并逐个处理,尽管你不应该在这里。
call 函数签名现在采用args: Any* 并将其转换为call 的主体中的AnyRef* 作为args map {_.asInstanceOf[AnyRef]}。希望这很好。不知道其中的微妙之处,所以问。
scala.collection.immutable.$colon$colon 传递给方法,而不是List[Type]。有什么想法吗?
是的。这叫做反射。 Here's a link to one way, using some experimental stuff 但是您应该记住,Scala 不是一种动态语言,并且可能无法轻松完成脚本语言可以完成的一些事情。您最好对字符串进行匹配,然后调用正确的方法。
【讨论】:
是的,你可以!
您将需要方法对象的.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
【讨论】:
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
【讨论】: