Groovyでは、文字列を使用してクラス/オブジェクトのメソッドを呼び出すことができることを知っています。例えば:
Foo."get"(1)
/* or */
String meth = "get"
Foo."$meth"(1)
クラスでこれを行う方法はありますか?クラスの名前を文字列として持っており、そのクラスを動的に呼び出せるようにしたいと考えています。たとえば、次のようなことをしようとしています。
String clazz = "Foo"
"$clazz".get(1)
私は本当に明白な何かが欠けていると思います、ただそれを理解することができません。
これを試して:
_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
}
_
GroovyMLでGuillaumeLaforgeが提案したように、
("Foo" as Class).get(i)
同じ結果が得られます。
私はこのコードでテストしました:
def name = "Java.lang.Integer"
def s = ("$name" as Class).parseInt("10")
println s
インスタンスの作成を示すChanwitの回答の拡張:
def dateClass = 'Java.util.Date' as Class
def date = dateClass.newInstance()
println date
これが別の方法です
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def target = application.domainClasses.find{it.name == 'ClassName'}
target.clazz.invokeMethod("Method",args)
これにより、パッケージ名を指定する必要はありません。ただし、2つの異なるパッケージに同じクラス名がある場合は注意してください。
私はバージョン1.8.8groovyを実行しています...そして簡単な例が機能します。
Import my.Foo
def myFx="myMethodToCall"
def myArg = 12
Foo."$myFx"(myArg)
必要に応じてFoo.myMethodToCall(12)を呼び出します。しかし、これが常に当てはまるかどうかはわかりません。
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は純粋に楽しいです;-)