2009-02-23 11 views
25

Wiem, że w Groovy można wywołać metodę na klasie/obiekcie za pomocą ciągu. Na przykład:Groovy sposób dynamicznego wywoływania metody statycznej

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

Czy można to zrobić w klasie? Mam nazwę klasy jako ciąg i chciałbym móc dynamicznie wywoływać tę klasę. Na przykład, patrząc na coś takiego:

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

Chyba brakuje czegoś naprawdę oczywiste, po prostu nie jestem w stanie zrozumieć.

+0

zajęcia nie otrzymują "wywołania" - tylko metody. Do czego dokładnie chcesz się odwołać? chcesz zrobić coś podobnego do MyOwnClass.static_property()? lub myInstanceOfClass.methodName()? – Chii

+1

Przypuszczam, że chce wywołać statyczną metodę na zajęciach. –

+0

Chcę wywołać metodę statyczną na klasie, klasy I, której nie znam do czasu wykonywania. Wiem, że sposobem Java jest użycie Class.forName, był po prostu ciekawy, czy istnieje sposób Groovy to zrobić, jak ich jest dla metod. –

Odpowiedz

14

Spróbuj tego:

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

trochę dłużej, ale powinno działać.

Jeśli chcesz utworzyć kod podobny do przełącznika dla metod statycznych, proponuję utworzyć instancje klas (nawet jeśli mają tylko statyczne metody) i zapisać instancje na mapie. Następnie można użyć jednej z nich, aby wybrać jedną z nich.

[EDIT]"$name" to GString i jako takie ważne oświadczenie. "$name".foo() oznacza „wywołać metodę foo() klasy GString

[EDIT2] Przy użyciu pojemnika internetową (jak Grails), trzeba określić classloader Istnieją dwie opcje:..

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

lub

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

pierwsza opcja będzie działać tylko w kontekście internetowej, drugie podejście działa również dla testów jednostkowych. zależy to od tego, że można zwykle użyj tego samego programu ładującego klasy co klasa, która wywołuje forName().

Jeśli masz problemy, a następnie użyć pierwszą opcję i ustaw contextClassLoader w badanej jednostki:

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

    ... test ... 
} finally { 
    Thread.currentThread().contextClassLoader = orig 
} 
+0

Miałem nadzieję dowiedzieć się, czy istnieje sposób "Groovy" robienia Class.forName, podobny do tego, jak bardzo ułatwiają nam refleksję nad metodami. Doceniam twoją odpowiedź i podejrzewam, że to może być jedyny sposób. –

+0

@John: Nie, to niemożliwe. Zapytałem o Groovy ML. Musisz zadzwonić do Class.forName(), ponieważ "$ name" to GString, a Groovy nie stara się być inteligentnym w kwestii zawartości zmiennej "name". –

+0

@Aaron - dziękuję, przeczytałem wątek na Groovy ML i to tylko informacje, których szukałem. –

29

Jak sugeruje Guillaume Laforge na Groovy ML

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

dałoby taki sam wynik.

Przetestowałem z tym kodem:

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

@chanwit - dzięki za przykład był bardzo pomocny. –

+1

Pierwszy przykład ("Foo" jako klasa) .get (i) wydaje się nie działać w grails do dynamicznego ładowania klasy domeny. –

+3

Działa to w grails 1.3.4 grailsApplication.classLoader.loadClass (name); –

2

Oto kolejny sposób

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

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

Dzięki temu nie ma potrzeby, aby określić nazwę pakietu. Bądź jednak ostrożny, jeśli masz tę samą nazwę klasy w dwóch różnych pakietach.

+0

Używam Grails 1.2.1 i Groovy 1.6.3, a otrzymałem klasy nie znaleziono wyjątków przy użyciu ClassForName() i jako metody Cast klasy powyżej. To szczególne podejście zadziałało dla mnie. –

3

następuje powiększenie do Chanwit za odpowiedź, przedstawiającym tworzenie instancji:

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

MELIX na Groovy ML wskazał mnie w kierunku „prawo” na metodzie dynamicznej klasy invokation chwilę z powrotem, całkiem przydatne:

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

Można utworzyć kolejną metodę metaClass łańcuchów, aby utworzyć instancje, odpowiednio refactoring:

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

Groovy to czysta zabawa; -)

1

Używam wersji 1.8.8 groovy ... i działa prosty przykład.

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

Foo."$myFx"(myArg) 

Wywołuje Foo.myMethodToCall (12) zgodnie z oczekiwaniami i oczekiwaniami. Nie wiem, czy tak było zawsze.

Powiązane problemy