groovy - traduccion - Una forma maravillosa de invocar dinámicamente un método estático
groovy traduccion (6)
Sé que en Groovy puedes 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 resolverlo.
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, ten cuidado si tienes el mismo nombre de clase en dos paquetes diferentes.
Como lo sugirió 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
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 se desea. No sé si esto siempre ha sido así.
Melix en Groovy ML me apuntó en la dirección "derecha" sobre la invocación dinámica del método de clase por 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()
También puede crear otro método de cadena metaClass 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; -)
Prueba esto:
def cl = Class.forName("org.package.Foo")
cl.get(1)
Un poco más pero debería funcionar.
Si desea crear un código tipo "interruptor" para métodos estáticos, sugiero crear una instancia de las clases (incluso si tienen solo métodos estáticos) y guardar las instancias en un mapa. Puedes usar
map[name].get(1)
para seleccionar uno de ellos.
[EDIT] "$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), debe especificar el cargador de clases. 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 generalmente pueda usar el mismo cargador de clases que la clase que invoca forName()
.
Si tiene problemas, use la primera opción y configure el contextClassLoader
en su prueba de unidad:
def orig = Thread.currentThread().contextClassLoader
try {
Thread.currentThread().contextClassLoader = getClass().classLoader
... test ...
} finally {
Thread.currentThread().contextClassLoader = orig
}
Un aumento a 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