Groovy manera de invocar dinámicamente un método estático


Sé que en Groovy se puede invocar un método en una clase/objeto usando una cadena. Por ejemplo:

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, buscando hacer algo como:

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

Creo que me estoy perdiendo algo realmente obvio, simplemente no soy capaz de averiguarlo.

 25
Author: cdeszaq, 2009-02-23

6 answers

Prueba esto:

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

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

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

map[name].get(1)

Para seleccionar uno de ellos.

[EDITAR] "$name" es un GString y, como tal, una 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), tiene que especificar el classloader. 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 funcionará solo en un contexto web, el segundo enfoque también funciona para pruebas unitarias. Depende del hecho de que normalmente se puede usar el mismo classloader que la clase que invoca forName().

Si tiene problemas, use la primera opción y establezca el contextClassLoader en su prueba unitaria:

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

    ... test ...
} finally {
    Thread.currentThread().contextClassLoader = orig
}
 15
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-30 07:06:11

Como sugiere Guillaume Laforge en Groovy ML,

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

Daría el mismo resultado.

He probado con este código:

def name = "java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
 29
Author: chanwit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-23 23:54:45

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

def dateClass = 'java.util.Date' as Class
def date = dateClass.newInstance()
println date
 3
Author: Paul King,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-29 07:18:10

Aquí hay 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 necesita especificar el nombre del paquete. Sin embargo, tenga cuidado si tiene el mismo nombre de clase en dos paquetes diferentes.

 2
Author: ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-13 21:49:17

Melix en Groovy ML me señaló en la dirección" correcta " en la invocación del método de clase dinámica hace un tiempo, 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()

También puede crear otro método de metaClase de cadena para crear instancias, refactorizando según corresponda:

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

Groovy es pura diversión; {)

 1
Author: virtualeyes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-26 18:23:50

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

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

Foo."$myFx"(myArg)

Llama a Foo.myMethodToCall (12) como se espera y se desea. Aunque no se si este ha sido siempre el caso.

 1
Author: alcoholiday,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-18 23:37:13