2009-02-23 31 views
25

Sé en Groovy que puede invocar un método en una clase/objeto utilizando una cadena. Por ejemplo:Forma maravillosa de invocar dinámicamente un método estático

Foo."get"(1) 
    /* or */ 
String meth = "get" 
Foo."$meth"(1) 

¿Hay alguna manera de hacer esto con la clase? Tengo el nombre de la clase como una cadena y me gustaría poder invocar dinámicamente esa clase. Por ejemplo, en busca de hacer algo como:

String clazz = "Foo" 
"$clazz".get(1) 

Creo que me falta algo muy obvio, pero no soy capaz de entenderlo.

+0

clases no se "invocan" - solo métodos. ¿Qué es exactamente lo que quieres invocar? ¿quieres hacer algo como MyOwnClass.static_property()? o myInstanceOfClass.methodName()? – Chii

+1

Supongo que quiere invocar un método estático en una clase. –

+0

Quiero invocar un método estático en una clase, una clase I que no sé hasta el tiempo de ejecución. Sé que la forma Java es usar Class.forName, era curioso si había una manera Groovy de hacer esto como si fuera por métodos. –

Respuesta

14

Prueba esto:

def cl = Class.forName("org.package.Foo") 
cl.get(1) 

Un poco más largo, pero debería funcionar.

Si desea crear un código tipo "switch" para métodos estáticos, sugiero crear una instancia de las clases (incluso si solo tienen métodos estáticos) y guardar las instancias en un mapa. A continuación, puede usar

map[name].get(1) 

para seleccionar uno de ellos.

[EDIT]"$name" es una GString y, como tal declaración válida. "$name".foo() significa "llamar al método foo() de la clase GString

[Edit2] Cuando se utiliza un contenedor web (como Grails), se tiene que especificar el cargador de clase Hay dos opciones:..

Class.forName("com.acme.MyClass", true, Thread.currentThread().contextClassLoader) 

o

Class.forName("com.acme.MyClass", true, getClass().classLoader) 

la primera opción sólo funcionará en un contexto web, el segundo método también funciona para las pruebas unitarias. depende del hecho de que por lo general puede use el mismo cargador de clases que la clase que invoca forName().

Si tiene problemas, a continuación, utilizar la primera opción y establecer el contextClassLoader en su prueba de unidad:

def orig = Thread.currentThread().contextClassLoader 
try { 
    Thread.currentThread().contextClassLoader = getClass().classLoader 

    ... test ... 
} finally { 
    Thread.currentThread().contextClassLoader = orig 
} 
+0

Tenía la esperanza de descubrir si existía una forma "maravillosa" de hacer Class.forName, similar a cómo hicieron que la reflexión sobre los métodos fuera tan fácil. Aprecio tu respuesta y sospecho que esta podría ser la única forma. –

+0

@John: No, eso no es posible. Pregunté por Groovy ML. Debe llamar a Class.forName() porque "$ name" es un GString y Groovy no intenta ser inteligente con respecto al contenido de la variable "nombre". –

+0

@ Aaron - gracias, leí el hilo en el Groovy ML y esa es la información que estaba buscando. –

29

Como se sugiere por Guillaume Laforge en Groovy ML,

("Foo" as Class).get(i) 

daría la misma resultado.

He probado con este código:

def name = "java.lang.Integer" 
def s = ("$name" as Class).parseInt("10") 
println s 
+0

@chanwit - gracias por el ejemplo fue muy útil. –

+1

El primer ejemplo ("Foo" como clase) .get (i) no parece funcionar en grillas para cargar dinámicamente una clase de dominio. –

+3

Esto funciona en grails 1.3.4 grailsApplication.classLoader.loadClass (name); –

2

Ésta es otra manera

import org.codehaus.groovy.grails.commons.ApplicationHolder as AH 

def target = application.domainClasses.find{it.name == 'ClassName'} 
target.clazz.invokeMethod("Method",args) 

Con esto no es necesario especificar el nombre del paquete. Sin embargo, ten cuidado si tienes el mismo nombre de clase en dos paquetes diferentes.

+0

Estoy ejecutando Grails 1.2.1 y Groovy 1.6.3, y estaba obteniendo excepciones de clase no encontrada usando ClassForName() y como métodos de conversión de clases anteriores. Este enfoque particular funcionó para mí. –

3

un aumento de la respuesta que ilustra la creación de una instancia de Chanwit:

def dateClass = 'java.util.Date' as Class 
def date = dateClass.newInstance() 
println date 
1

Melix en Groovy ML me señaló en la dirección "correcta" en el método de clase dinámica invocación hace un tiempo atrás, bastante útil:

// define in script (not object) scope 
def loader = this.getClass().getClassLoader() 

// place this in some MetaUtils class, invoked on app startup 
String.metaClass.toClass = { 
    def classPath = getPath(delegate) // your method logic to determine 'path.to.class' 
    Class.forName(classPath, true, this.loader) 
} 

// then, anywhere in your app 
"Foo".toClass().bar() 

Puede crear otro método metaClass de cadena para crear instancias también, refactorizar según corresponda:

String.metaClass.toObject = { 
    def classPath = getPath(delegate) 
    Class.forName(classPath, true, this.loader).newInstance() 
} 

Groovy es pura diversión; -)

1

Estoy ejecutando la versión 1.8.8 groovy ... y el simple ejemplo funciona.

Import my.Foo 
def myFx="myMethodToCall" 
def myArg = 12 

Foo."$myFx"(myArg) 

Llama a Foo.myMethodToCall (12) como se esperaba y deseado. No sé si esto siempre ha sido así.

Cuestiones relacionadas