Scala:如何动态实例化对象并使用反射调用方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1469958/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Scala: How do I dynamically instantiate an object and invoke a method using reflection?
提问by Eugene Yokota
In Scala, what's the best way to dynamically instantiate an object and invoke a method using reflection?
在 Scala 中,动态实例化对象并使用反射调用方法的最佳方法是什么?
I would like to do Scala-equivalent of the following Java code:
我想做以下 Java 代码的 Scala 等效项:
Class class = Class.forName("Foo");
Object foo = class.newInstance();
Method method = class.getMethod("hello", null);
method.invoke(foo, null);
In the above code, both the class name and the method name are passed in dynamically. The above Java mechanism could probably be used for Fooand hello(), but the Scala types don't match one-to-one with that of Java. For example, a class may be declared implicitly for a singleton object. Also Scala method allows all sorts of symbols to be its name. Both are resolved by name mangling. See Interop Between Java and Scala.
上面代码中,类名和方法名都是动态传入的。上面的 Java 机制可能可以用于Fooand hello(),但是 Scala 类型与 Java 的类型不一一匹配。例如,可以为单例对象隐式声明一个类。Scala 方法还允许使用各种符号作为其名称。两者都通过名称修改来解决。请参阅Java 和 Scala 之间的互操作。
Another issue seems to be the matching of parameters by resolving overloads and autoboxing, described in Reflection from Scala - Heaven and Hell.
另一个问题似乎是通过解决重载和自动装箱来匹配参数,这在Scala 的 Reflection - Heaven and Hell 中有所描述。
回答by Walter Chang
There is an easier way to invoke method reflectively without resorting to calling Java reflection methods: use Structural Typing.
有一种更简单的方法可以反射调用方法,而无需调用 Java 反射方法:使用结构类型。
Just cast the object reference to a Structural Type which has the necessary method signature then call the method: no reflection necessary (of course, Scala is doing reflection underneath but we don't need to do it).
只需将对象引用转换为具有必要方法签名的结构类型,然后调用该方法:不需要反射(当然,Scala 在下面进行反射,但我们不需要这样做)。
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"
}
}
回答by Daniel C. Sobral
The answers by VonCand Walter Changare quite good, so I'll just complement with one Scala 2.8 Experimental feature. In fact, I won't even bother to dress it up, I'll just copy the scaladoc.
VonC和Walter Chang的回答非常好,所以我将补充一个 Scala 2.8 实验功能。事实上,我什至懒得修饰它,我只会复制scaladoc。
object Invocation
extends AnyRef
A more convenient syntax for reflective invocation. Example usage:
一种更方便的反射调用语法。用法示例:
class Obj { private def foo(x: Int, y: String): Long = x + y.length }
You can call it reflectively one of two ways:
您可以通过以下两种方式之一反射性地调用它:
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.
If you call the oo method and do not give the type inferencer enough help, it will most likely infer Nothing, which will result in a ClassCastException.
Author Paul Phillips
如果您调用 oo 方法并且没有给类型推断器足够的帮助,它很可能会推断出 Nothing,这将导致 ClassCastException。
作者保罗菲利普斯
回答by nedim
In case you need to invoke a method of a Scala 2.10 object (not class) and you have the names of the method and object as Strings, you can do it like this:
如果您需要调用 Scala 2.10 对象(不是类)的方法并且您将方法和对象的名称设为Strings,您可以这样做:
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)
}
}
This prints 5to standard output.
Further details in Scala Documentation.
这将打印5到标准输出。Scala 文档中的更多详细信息。
回答by VonC
The instanciation part coulduse the Manifest: see this SO answer
experimental feature in Scala called manifests which are a way to get around a Java constraint regarding type erasure
Scala 中的实验性功能称为清单,这是一种绕过 Java 类型擦除约束的方法
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
With this version you still write
用这个版本你仍然写
class Foo
val t = new Test[Foo]
However, if there's no no-arg constructor available you get a runtime exception instead of a static type error
但是,如果没有可用的无参数构造函数,则会得到运行时异常而不是静态类型错误
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)
So the true type safe solution would be using a Factory.
所以真正的类型安全解决方案是使用工厂。
Note: as stated in this thread, Manifest is here to stay, but is for now "only use is to give access to the erasure of the type as a Class instance."
注意:如该线程中所述,Manifest 将保留,但目前“仅用于将类型的擦除作为 Class 实例进行访问”。
The only thing manifests give you now is the erasure of the statictype of a parameter at the call site (contrary to
getClasswhich give you the erasure of the dynamictype).
现在清单给你的唯一一件事是在调用站点擦除参数的静态类型(相反,
getClass它给你动态类型的擦除)。
You can then get a method through reflection:
然后就可以通过反射得到一个方法:
classOf[ClassName].getMethod("main", classOf[Array[String]])
and invoke it
并调用它
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
回答by matanster
Working up from @nedim's answer, here is a basisfor a full answer, main difference being here below we instantiate naive classes. This code does not handle the case of multiple constructors, and is by no means a full answer.
从@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", "")
}
Here is a build.sbtthat would compile it:
这是一个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"
)
)

