斯卡拉:如何动态地实例化一个对象,并使用反射调用的方法?(Scala: How do I dynam

2019-07-19 06:28发布

在Scala中,什么是动态实例化一个对象,并使用反射调用的方法的最佳方式?

我愿做下面的Java代码的斯卡拉相当于:

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

在上面的代码,无论是类名和方法名中动态地被传递。 上面的Java机制很可能被用于Foohello()但Scala的类型不匹配,一个对一个与爪哇。 例如,一类可被隐式地声明了单独的对象。 此外Scala的方法允许符号的各种不同的人是它的名字。 两者都是由名字改编解决。 见互操作的Java和Scala之间 。

另一个问题似乎是参数通过解决过载和自动装箱,描述的匹配,从斯卡拉思考-天堂与地狱 。

Answer 1:

还有就是要反思调用方法,而不诉诸调用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"
  }
}


Answer 2:

通过这些问题的答案VonC和沃尔特·昌都相当不错,所以我就用一个斯卡拉2.8实验功能互补。 事实上,我甚至不会刻意去打扮起来,我就复制scaladoc。

object Invocation
  extends AnyRef

更方便的语法为反射调用。 实例:

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方法,不给类型inferencer足够的帮助,它很可能会推断出没有什么,这将导致一个ClassCastException。

作者保罗·菲利普斯



Answer 3:

该instanciation部分可以使用清单 :看到这个SO答案

Scala的实验性功能称为舱单这是一种方式来获得有关类型擦除包含Java约束

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

在这个版本中,你还是写

class Foo
val t = new Test[Foo]

然而,如果没有无参数的构造提供你会得到一个运行时异常而不是静态类型的错误

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

所以,真正的类型安全的解决办法是使用工厂。


注:在陈述这个线程 ,清单是在这里留下来,但现在“唯一的用处就是给访问类型的类实例的删除。”

唯一清单给你现在是在调用点(与静态类型参数的擦除getClass ,给您的动态类型的擦除)。


然后,您可以得到通过反射的方法:

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 


Answer 4:

如果你需要调用斯卡拉2.10对象(而不是类)的方法和你的方法和对象的名称String S,你可以做这样的:

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到标准输出。 在进一步的细节斯卡拉文档 。



Answer 5:

从@内迪姆的回答工作了,这里是一个完整的答案的基础 ,主要区别在于这里下面我们实例幼稚类。 这个代码不处理多个构造的情况下,并且绝非一个完整的答案。

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: How do I dynamically instantiate an object and invoke a method using reflection?