2009-02-23 115 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)。我不知道這是否一直如此。