2009-02-23 117 views
25

我知道在Groovy中,您可以使用字符串在类/对象上调用方法。例如:Groovy方法动态调用静态方法

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

有没有办法与这个类做到这一点?我把这个类的名字看作一个字符串,并希望能够动态地调用这个类。例如,寻找类似的东西:

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

我想我错过了一些非常明显的东西,只是无法弄清楚。

+0

类不会被“调用” - 只有方法。你想要调用什么?你想要做一些像MyOwnClass.static_property()?或myInstanceOfClass.methodName()? – Chii 2009-02-23 12:49:17

+1

我的猜测是他想调用一个类的静态方法。 – 2009-02-23 12:52:54

+0

我想调用一个类,一个我不知道直到运行时间的类的静态方法。我知道Java的方式是使用Class.forName,只是好奇,如果有Groovy的方式来做到这一点像他们的方法。 – 2009-02-23 16:18:18

回答

14

试试这个:

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

有点长,但应该工作。

如果你想为静态方法创建类似“switch”的代码,我建议实例化这些类(即使它们只有静态方法)并将这些实例保存在一张地图中。然后您可以使用

map[name].get(1) 

选择其中之一。

[编辑]"$name"是一个GString和这样一个有效的陈述。 "$name".foo()意味着“调用方法的类GStringfoo()

[EDIT2]当使用Web容器(如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 
} 
+0

我希望找出是否有一个“Groovy”做Class.forName的方式,类似于他们如何轻松地反思方法。我很欣赏你的回答,并怀疑这可能是唯一的方法。 – 2009-02-23 16:20:38

29

由于Groovy的ML建议由纪尧姆·拉法格,

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

将给予相同结果。

我这个代码进行测试:

def name = "java.lang.Integer" 
def s = ("$name" as Class).parseInt("10") 
println s 
2

这里的另一种方式

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

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

有了这个,你不需要指定包名。但要小心,如果你在两个不同的包中有相同的类名。

3

的增强,以实例的Chanwit的回答说明创作:

def dateClass = 'java.util.Date' as Class 
def date = dateClass.newInstance() 
println date 
1

Melix Groovy的ML指出我在动态类方法invokation“正确”方向一段时间回来,非常有用:

// 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是纯粹的乐趣; - )

1

我正在运行版本1.8.8 groovy ...和简单的例子工程。

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

Foo."$myFx"(myArg) 

按预期和期望调用Foo.myMethodToCall(12)。我不知道这是否一直如此。