web-dev-qa-db-ja.com

静的メソッドを動的に呼び出すGroovyの方法

Groovyでは、文字列を使用してクラス/オブジェクトのメソッドを呼び出すことができることを知っています。例えば:

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

クラスでこれを行う方法はありますか?クラスの名前を文字列として持っており、そのクラスを動的に呼び出せるようにしたいと考えています。たとえば、次のようなことをしようとしています。

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

私は本当に明白な何かが欠けていると思います、ただそれを理解することができません。

26

これを試して:

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

少し長くなりますが、動作するはずです。

静的メソッドの「スイッチ」のようなコードを作成する場合は、クラスをインスタンス化し(静的メソッドしかない場合でも)、インスタンスをマップに保存することをお勧めします。その後、使用することができます

_map[name].get(1)
_

それらの1つを選択します。

[EDIT] _"$name"_はGStringであり、そのため有効なステートメントです。 "$name".foo()は、「クラスGStringのメソッドfoo()を呼び出すことを意味します。

[EDIT2] Webコンテナ(Grailsなど)を使用する場合は、クラスローダーを指定する必要があります。 2つのオプションがあります。

_Class.forName("com.acme.MyClass", true, Thread.currentThread().contextClassLoader)
_

または

_Class.forName("com.acme.MyClass", true, getClass().classLoader)
_

最初のオプションはWebコンテキストでのみ機能し、2番目のアプローチは単体テストでも機能します。これは、通常、forName()を呼び出すクラスと同じクラスローダーを使用できるという事実に依存します。

問題がある場合は、最初のオプションを使用して、単体テストでcontextClassLoaderを設定します。

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

    ... test ...
} finally {
    Thread.currentThread().contextClassLoader = orig
}
_
16
Aaron Digulla

GroovyMLでGuillaumeLaforgeが提案したように、

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

同じ結果が得られます。

私はこのコードでテストしました:

def name = "Java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
29
chanwit

インスタンスの作成を示すChanwitの回答の拡張:

def dateClass = 'Java.util.Date' as Class
def date = dateClass.newInstance()
println date
3
Paul King

これが別の方法です

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

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

これにより、パッケージ名を指定する必要はありません。ただし、2つの異なるパッケージに同じクラス名がある場合は注意してください。

2
ken

私はバージョン1.8.8groovyを実行しています...そして簡単な例が機能します。

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

Foo."$myFx"(myArg)

必要に応じてFoo.myMethodToCall(12)を呼び出します。しかし、これが常に当てはまるかどうかはわかりません。

1
alcoholiday

Groovy MLのMelixは、しばらく前に動的クラスメソッドの呼び出しについて「正しい」方向を示してくれました。これは非常に便利です。

// 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
virtualeyes