55 votes

Scala: Comment instancier dynamiquement un objet et invoquer une méthode à l'aide de la réflexion?

En Scala, quelle est la meilleure façon d'instancier dynamiquement un objet et appeler une méthode à l'aide de la réflexion?

Je voudrais faire Scala-équivalent du code Java suivant:

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

Dans le code ci-dessus, à la fois le nom de la classe et le nom de la méthode sont transmises de façon dynamique. La au-dessus de Java mécanisme pourrait sans doute être utilisé pour Foo et hello(), mais la Scala types ne correspondent pas à un-à-un avec celle de Java. Par exemple, une classe peut être déclarée implicitement pour un objet singleton. Aussi Scala méthode permet toutes sortes de symboles à son nom. Les deux sont résolus par le nom d'amputation. Voir l'Interopérabilité Entre Java et Scala.

Un autre problème semble être la mise en correspondance des paramètres par la résolution de surcharges et de l'autoboxing, décrit dans la Réflexion de Scala - Ciel et de l'Enfer.

72voto

Walter Chang Points 7041

Il existe un moyen plus simple d'appeler une méthode de manière réflexive sans recourir à l'appel de méthodes de réflexion Java: utilisez le typage structurel.

Il suffit de transtyper la référence de l'objet à un type de structure possédant la signature de méthode nécessaire, puis d'appeler la méthode: aucune réflexion n'est nécessaire (bien sûr, Scala effectue une réflexion en dessous mais nous n'avons pas besoin de la faire).

 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"
  }
}
 

12voto

Daniel C. Sobral Points 159554

Les réponses par VonC et Walter Chang sont assez bon, donc je vais juste compléter avec un Scala 2.8 fonctionnalité Expérimentale. En fait, je ne vais même pas la peine de l'habiller, je vais juste copier le scaladoc.

object Invocation
  extends AnyRef

Le plus commode de la syntaxe pour les invocation. Exemple d'utilisation:

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

Vous pouvez l'appeler reflectively l'un des de deux façons:

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.

Si vous appelez de la oo méthode et ne donnent pas le type inferencer assez d'aide, il sera plus probablement Rien inférer, qui suite à une ClassCastException.

Auteur Paul Phillips

7voto

VonC Points 414372

L'instanciation de la partie pourrait utiliser le Manifeste: voir cette SORTE de réponse

fonctionnalité expérimentale en Scala appelé manifeste qui sont un moyen de contourner un Java contrainte sur le type d'effacement

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

Avec cette version, vous avez encore écrire

class Foo
val t = new Test[Foo]

Cependant, si il n'y a pas de non-arg constructeur disponible vous recevez une exception d'exécution au lieu d'une erreur de type statique

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

Donc, le vrai type de solution de sécurité seraient à l'aide d'une Usine.


Remarque: comme indiqué dans ce fil, Manifeste est là pour rester, mais est pour le moment "seule utilité est de donner accès à l'effacement du type comme une instance de Classe."

La seule chose qui se manifeste de vous donner maintenant est l'effacement de la statique type d'un paramètre à l'appel du site (contrairement à getClass qui vous donnent l'effacement de la dynamique type).


Vous pouvez obtenir une méthode de réflexion:

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

et à l'appeler

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X