Подтвердить что ты не робот

Groovy способ динамического вызова статического метода

Я знаю, что в Groovy вы можете вызвать метод для класса/объекта, используя строку. Например:

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

Есть ли способ сделать это с классом? У меня есть имя класса как строка и хотелось бы динамически вызывать этот класс. Например, вы хотите сделать что-то вроде:

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

Я думаю, что мне не хватает чего-то действительно очевидного, просто я не могу понять это.

4b9b3361

Ответ 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), как ожидалось и желательно. Я не знаю, было ли это всегда так.