Scala:我如何dynamic实例化对象并使用reflection调用方法?

在Scala中,dynamic实例化对象并使用reflection调用方法的最佳方法是什么?

我想做下面的Java代码的Scala相当于:

Class class = Class.forName("Foo"); Object foo = class.newInstance(); Method method = class.getMethod("hello", null); method.invoke(foo, null); 

在上面的代码中,类名和方法名都是dynamic传入的。 上面的Java机制可能可以用于Foohello() ,但是Scalatypes不能与Java的一对一匹配。 例如,一个类可以被隐式地声明为一个单例对象。 另外Scala方法允许各种各样的符号成为它的名字。 两者都是通过名称修改来解决的。 请参阅Java和Scala之间的互操作 。

另一个问题似乎是通过解决重载和自动装箱参数的匹配问题,在Scala的反思 – 天堂与地狱中描述 。

有一个更简单的方法来reflection调用方法,而不诉诸于Javareflection方法:使用结构式。

只需将对象引用转换为具有必要方法签名的结构types,然后调用方法:不需要reflection(当然,Scala正在底层做reflection,但我们不需要这样做)。

 class Foo { def hello(name: String): String = "Hello there, %s".format(name) } object FooMain { def main(args: Array[String]) { val foo = Class.forName("Foo").newInstance.asInstanceOf[{ def hello(name: String): String }] println(foo.hello("Walter")) // prints "Hello there, Walter" } } 

VonC和Walter Chang的答案相当不错,所以我只补充一个Scala 2.8实验性function。 事实上,我甚至不打扮打扮,我只是复制scaladoc。

 object Invocation extends AnyRef 

reflection调用的更方便的语法。 用法示例:

 class Obj { private def foo(x: Int, y: String): Long = x + y.length } 

您可以通过以下两种方式之一反思:

 import scala.reflect.Invocation._ (new Obj) o 'foo(5, "abc") // the 'o' method returns Any val x: Long = (new Obj) oo 'foo(5, "abc") // the 'oo' method casts to expected type. 

如果你调用oo方法,并且不给types推理器足够的帮助,它很可能会推断Nothing,这将导致ClassCastException。

作者Paul Phillips

实例部分可以使用Manifest :看到这个答案

Scala中的实验性function称为清单(manifest),这是一种绕过types擦除的Java约束的方法

  class Test[T](implicit m : Manifest[T]) { val testVal = m.erasure.newInstance().asInstanceOf[T] } 

有了这个版本,你仍然写

 class Foo val t = new Test[Foo] 

但是,如果没有可用的无参数构造函数,则会得到运行时exception,而不是静态types错误

 scala> new Test[Set[String]] java.lang.InstantiationException: scala.collection.immutable.Set at java.lang.Class.newInstance0(Class.java:340) 

所以真正的types安全解决scheme将使用工厂。


注意:正如在这个线程中所说的,Manifest在这里停留,但现在是“只能用来作为一个Class实例的types的擦除”。

现在唯一的事情就是在调用地址清除参数的静态types(与getClass相反,它使您可以删除dynamictypes)。


然后你可以通过反思得到一个方法:

 classOf[ClassName].getMethod("main", classOf[Array[String]]) 

并调用它

 scala> class A { | def foo_=(foo: Boolean) = "bar" | } defined class A scala>val a = new A a: A = A@1f854bd scala>a.getClass.getMethod(decode("foo_="), classOf[Boolean]).invoke(a, java.lang.Boolean.TRUE) res15: java.lang.Object = bar 

从@ nedim的答案,这里是一个完整的答案的基础 ,主要区别在这里下面我们实例化幼稚的类。 此代码不处理多个构造函数的情况下,并不是一个完整的答案。

 import scala.reflect.runtime.universe case class Case(foo: Int) { println("Case Case Instantiated") } class Class { println("Class Instantiated") } object Inst { def apply(className: String, arg: Any) = { val runtimeMirror: universe.Mirror = universe.runtimeMirror(getClass.getClassLoader) val classSymbol: universe.ClassSymbol = runtimeMirror.classSymbol(Class.forName(className)) val classMirror: universe.ClassMirror = runtimeMirror.reflectClass(classSymbol) if (classSymbol.companion.toString() == "<none>") // TODO: use nicer method "hiding" in the api? { println(s"Info: $className has no companion object") val constructors = classSymbol.typeSignature.members.filter(_.isConstructor).toList if (constructors.length > 1) { println(s"Info: $className has several constructors") } else { val constructorMirror = classMirror.reflectConstructor(constructors.head.asMethod) // we can reuse it constructorMirror() } } else { val companionSymbol = classSymbol.companion println(s"Info: $className has companion object $companionSymbol") // TBD } } } object app extends App { val c = Inst("Class", "") val cc = Inst("Case", "") } 

这是一个build.sbt ,可以编译它:

 lazy val reflection = (project in file(".")) .settings( scalaVersion := "2.11.7", libraryDependencies ++= Seq( "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided", "org.scala-lang" % "scala-library" % scalaVersion.value % "provided" ) ) 

如果你需要调用一个Scala 2.10对象的方法(不是类),并且你有方法和对象的名字,那么你可以这样做:

 package com.example.mytest import scala.reflect.runtime.universe class MyTest object MyTest { def target(i: Int) = println(i) def invoker(objectName: String, methodName: String, arg: Any) = { val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader) val moduleSymbol = runtimeMirror.moduleSymbol( Class.forName(objectName)) val targetMethod = moduleSymbol.typeSignature .members .filter(x => x.isMethod && x.name.toString == methodName) .head .asMethod runtimeMirror.reflect(runtimeMirror.reflectModule(moduleSymbol).instance) .reflectMethod(targetMethod)(arg) } def main(args: Array[String]): Unit = { invoker("com.example.mytest.MyTest$", "target", 5) } } 

这将打印5到标准输出。 Scala文档中的更多细节。