Groovy способ динамического вызова статического метода
Я знаю, что в Groovy вы можете вызвать метод для класса/объекта, используя строку. Например:
Foo."get"(1)
/* or */
String meth = "get"
Foo."$meth"(1)
Есть ли способ сделать это с классом? У меня есть имя класса как строка и хотелось бы динамически вызывать этот класс. Например, вы хотите сделать что-то вроде:
String clazz = "Foo"
"$clazz".get(1)
Я думаю, что мне не хватает чего-то действительно очевидного, просто я не могу понять это.
Ответы
Ответ 1
Попробуйте следующее:
def cl = Class.forName("org.package.Foo")
cl.get(1)
Немного дольше, но он должен работать.
Если вы хотите создать "switch" -подобный код для статических методов, я предлагаю создавать экземпляры классов (даже если они имеют только статические методы) и сохранять экземпляры на карте. Затем вы можете использовать
map[name].get(1)
чтобы выбрать один из них.
[EDIT] "$name"
- это GString
и как таковой действительный оператор. "$name".foo()
означает "вызывать метод foo()
класса GString
.
[EDIT2] При использовании веб-контейнера (например, Grails) вы должны указать загрузчик классов. Существует два варианта:
Class.forName("com.acme.MyClass", true, Thread.currentThread().contextClassLoader)
или
Class.forName("com.acme.MyClass", true, getClass().classLoader)
Первый вариант будет работать только в веб-контексте, второй подход также работает для модульных тестов. Это зависит от того, что вы обычно можете использовать тот же загрузчик классов, что и класс, вызывающий forName()
.
Если у вас есть проблемы, используйте первый вариант и установите contextClassLoader
в unit test:
def orig = Thread.currentThread().contextClassLoader
try {
Thread.currentThread().contextClassLoader = getClass().classLoader
... test ...
} finally {
Thread.currentThread().contextClassLoader = orig
}
Ответ 2
Как предположил Гийом Лафордж на Groovy ML,
("Foo" as Class).get(i)
даст тот же результат.
Я тестировал этот код:
def name = "java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
Ответ 3
Расширение ответа Chanwit, иллюстрирующего создание экземпляра:
def dateClass = 'java.util.Date' as Class
def date = dateClass.newInstance()
println date
Ответ 4
Здесь другой путь
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def target = application.domainClasses.find{it.name == 'ClassName'}
target.clazz.invokeMethod("Method",args)
При этом вам не нужно указывать имя пакета. Будьте осторожны, если у вас есть одно и то же имя класса в двух разных пакетах.
Ответ 5
Melix на Groovy ML указал мне в правильном направлении на вызов метода динамического класса некоторое время назад, весьма полезный:
// 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()
Вы также можете создать другой метод metaClass для создания экземпляров, рефакторинг соответствующим образом:
String.metaClass.toObject = {
def classPath = getPath(delegate)
Class.forName(classPath, true, this.loader).newInstance()
}
Groovy - чистое удовольствие; -)
Ответ 6
Я запускаю версию 1.8.8 groovy... и работает простой пример.
Import my.Foo
def myFx="myMethodToCall"
def myArg = 12
Foo."$myFx"(myArg)
Вызывает Foo.myMethodToCall(12), как ожидалось и желательно. Я не знаю, было ли это всегда так.