2009-09-24 2 views
44

В Scala, что является лучшим способом динамического создания объекта и вызова метода с использованием отражения?Scala: Как динамически создать экземпляр объекта и вызвать метод с использованием отражения?

Я хотел бы сделать Scala-эквивалент следующего кода Java:

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

В приведенном выше коде, как имя класса и имя метода передаются в динамически. Вышеупомянутый механизм Java, вероятно, можно было бы использовать для Foo и hello(), но типы Scala не соответствуют друг другу с Java. Например, класс может быть объявлен неявно для одноэлементного объекта. Кроме того, метод Scala позволяет использовать все виды символов. Оба разрешаются путем изменения имени. См. Interop Between Java and Scala.

Другой вопрос, похоже, соответствует параметрам путем разрешения перегрузок и автобоксинга, описанных в Reflection from Scala - Heaven and Hell.

+2

Учитывая, что экспериментальная функция в моем ответе не делал 2.8.0, было бы лучше, если бы другой ответ был отмечен как принято. –

+0

если у меня есть класс с параметрами для класса, например класс MailServerice (emailIds: string) можно ли динамически запускать во время выполнения? – ashK

ответ

57

Существует простой способ вызвать метод рефлекторно, не прибегая к вызывающему 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" 
    } 
} 
+11

Структурный тип не поможет мне, если я не знаю имя метода во время компиляции. –

+7

, используя псевдоним типа, чтобы придать структурному типу имя, часто улучшает читабельность этого трюка. –

+1

Это только ответы на часть вопроса. Есть ли способ Scala для выполнения метода Method = class.getMethod («hello», null) ;? –

6

часть при создании экземпляра может использовать Manifest: посмотреть SO answer

experimental feature in Scala called manifests which are a way to get around a Java constraint regarding type erasure

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 безопасное решение истинного типа будет использовать фабрику.


Примечание: как указано в this thread, Manifest здесь остаться, но сейчас «только использование, чтобы дать доступ к стиранию типа, как экземпляр класса.»

The only thing manifests give you now is the erasure of the static type of a parameter at the call site (contrary to getClass which give you the erasure of the dynamic type).


Вы можете получить метод посредством отражения:

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 = [email protected] 

scala>a.getClass.getMethod(decode("foo_="), 
classOf[Boolean]).invoke(a, java.lang.Boolean.TRUE) 
res15: java.lang.Object = bar 
12

Ответы на VonC и Walter Chang довольно хорошо, так что я просто дополнить одним Scala 2.8 Экспериментальная функция. На самом деле, я даже не буду одеваться, я просто копирую скаладок.

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

+0

Это то, что я имел в виду из связанной статьи блога. Еще один кусочек головоломки - это услуга, управляющая именем. –

+3

К сожалению, Invocation не закончил: https://lampsvn.epfl.ch/trac/scala/changeset/19695 –

3

В случае, если вам нужно вызвать метод объекта Scala 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 до стандартного выхода. Дополнительная информация: Scala Documentation.

+1

Как насчет класса, а не объекта? – matanster

4

Обработку от @ 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" 
    ) 
) 
+1

связанное продолжение: http://stackoverflow.com/questions/34227984/perfectly-handling-scala-constructors-in-dynamic-instantiation – matanster