Eug*_*ota 50 reflection scala name-mangling
在Scala中,动态实例化对象并使用反射调用方法的最佳方法是什么?
我想做Scala - 相当于以下Java代码:
Class class = Class.forName("Foo");
Object foo = class.newInstance();
Method method = class.getMethod("hello", null);
method.invoke(foo, null);
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,类名和方法名都是动态传递的.上面的Java机制可能用于Foo和hello(),但Scala类型与Java的一对一不匹配.例如,可以为单个对象隐式声明类.Scala方法也允许各种符号作为其名称.两者都通过名称修改来解决.请参阅Java和Scala之间的Interop.
另一个问题似乎是通过解决重载和自动装箱来匹配参数,如Scala - Heaven和Hell的反思所述.
Wal*_*ang 66
有一种更简单的方法可以反射地调用方法而无需调用Java反射方法:使用结构类型.
只需将对象引用转换为具有必要方法签名的结构类型,然后调用方法:不需要反射(当然,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"
}
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*ral 12
VonC和Walter Chang的答案非常好,所以我只补充一个Scala 2.8实验功能.事实上,我甚至不打算打扮,我只是复制scaladoc.
object Invocation
extends AnyRef
Run Code Online (Sandbox Code Playgroud)
反射调用的更方便的语法.用法示例:
class Obj { private def foo(x: Int, y: String): Long = x + y.length }
Run Code Online (Sandbox Code Playgroud)
你可以反思性地称之为两种方式之一:
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.
Run Code Online (Sandbox Code Playgroud)
如果你调用oo方法并且不给类型推理器足够的帮助,它很可能会推断Nothing,这将导致ClassCastException.
作者保罗菲利普斯
如果您需要调用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)
}
}
Run Code Online (Sandbox Code Playgroud)
这打印5到标准输出.Scala文档中的更多详细信息.
Scala中的实验性功能称为清单,它是一种绕过类型擦除的Java约束的方法
class Test[T](implicit m : Manifest[T]) {
val testVal = m.erasure.newInstance().asInstanceOf[T]
}
Run Code Online (Sandbox Code Playgroud)
有了这个版本,你仍然可以写
class Foo
val t = new Test[Foo]
Run Code Online (Sandbox Code Playgroud)
但是,如果没有no-arg构造函数可用,则会得到运行时异常而不是静态类型错误
scala> new Test[Set[String]]
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)
Run Code Online (Sandbox Code Playgroud)
因此真正的类型安全解决方案是使用工厂.
注意:正如在这个帖子中所说的那样,Manifest就在这里,但是现在"只是用来访问类型作为Class实例的擦除".
唯一能显示的是现在是在调用站点擦除参数的静态类型(相反,
getClass它会删除动态类型).
然后,您可以通过反射获得一个方法:
classOf[ClassName].getMethod("main", classOf[Array[String]])
Run Code Online (Sandbox Code Playgroud)
并调用它
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
Run Code Online (Sandbox Code Playgroud)
从@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", "")
}
Run Code Online (Sandbox Code Playgroud)
这是一个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"
)
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
46206 次 |
| 最近记录: |