Отличный способ динамического вызова статического метода - PullRequest
26 голосов
/ 23 февраля 2009

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

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

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

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

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

Ответы [ 6 ]

29 голосов
/ 24 февраля 2009

По предложению Гийома Лафоржа о Groovy ML,

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

даст тот же результат.

Я тестировал с этим кодом:

def name = "java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
16 голосов
/ 23 февраля 2009

Попробуйте это:

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

Немного дольше, но должно работать.

Если вы хотите создать подобный переключателю код для статических методов, я предлагаю создать экземпляры классов (даже если они имеют только статические методы) и сохранить экземпляры на карте. Затем вы можете использовать

map[name].get(1)

, чтобы выбрать один из них.

[РЕДАКТИРОВАТЬ] "$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 в своем модульном тесте:

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

    ... test ...
} finally {
    Thread.currentThread().contextClassLoader = orig
}
3 голосов
/ 29 декабря 2010

Дополнение к ответу Chanwit, иллюстрирующее создание экземпляра:

def dateClass = 'java.util.Date' as Class
def date = dateClass.newInstance()
println date
2 голосов
/ 14 августа 2010

Вот еще один способ

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

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

При этом вам не нужно указывать имя пакета. Будьте осторожны, если у вас одинаковое имя класса в двух разных пакетах.

1 голос
/ 19 января 2013

Я использую Groovy версии 1.8.8 ... и простой пример работает.

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

Foo."$myFx"(myArg)

Вызывает Foo.myMethodToCall (12), как ожидается и желательно. Я не знаю, было ли это всегда так.

1 голос
/ 26 апреля 2011

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()

Вы также можете создать другой метод метакласса строки для создания экземпляров, при необходимости изменив рефакторинг:

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

Groovy - это просто весело; -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...