2009-02-23 11 views
25

मुझे ग्रोवी में पता है कि आप एक स्ट्रिंग का उपयोग कर कक्षा/ऑब्जेक्ट पर एक विधि का आह्वान कर सकते हैं। उदाहरण के लिए:गतिशील रूप से एक स्थिर विधि का आह्वान करने के लिए ग्रोवी तरीका

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

क्या कक्षा के साथ ऐसा करने का कोई तरीका है? मेरे पास वर्ग का नाम एक स्ट्रिंग के रूप में है और वह उस कक्षा को गतिशील रूप से आमंत्रित करने में सक्षम होना चाहता है। उदाहरण के लिए, की तरह कुछ करने के लिए देख:

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

मुझे लगता है कि मैं वास्तव में कुछ स्पष्ट याद कर रहा हूँ, बस यह पता लगाने में सक्षम नहीं हूँ।

+0

कक्षाएं "आवंटित" नहीं होतीं - केवल विधियां। यह वास्तव में क्या है कि आप आह्वान करना चाहते हैं? क्या आप MyOwnClass.static_property() जैसी कुछ करना चाहते हैं? या myInstanceOfClass.methodName()? – Chii

+1

मेरा अनुमान है कि वह कक्षा पर एक स्थिर विधि का आह्वान करना चाहता है। –

+0

मैं कक्षा पर एक स्थैतिक विधि का आह्वान करना चाहता हूं, एक वर्ग I जिसे मैं रन टाइम तक नहीं जानता। मुझे पता है कि क्लास .forName का उपयोग करने के लिए जावा तरीका है, अगर यह ऐसा करने के लिए ग्रोवी तरीका था तो विधियों के लिए ऐसा ही उत्सुक था। –

उत्तर

14

इस प्रयास करें:

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

थोड़ा सा रह गया लेकिन काम करना चाहिए।

आप स्थिर तरीकों के लिए कोड की तरह "स्विच" बनाने के लिए चाहते हैं, तो मैं वर्गों का दृष्टांत (भले ही वे केवल स्थिर तरीकों) और नक्शे पर उदाहरणों को बचाने के लिए सुझाव देते हैं। तुम तो

map[name].get(1) 

का उपयोग उन में से एक को चुनने के लिए कर सकते हैं।

[संपादित करें]"$name" एक GString है और इस तरह एक वैध बयान के रूप में। "$name".foo() विधि वर्ग GString की foo() फोन का अर्थ है "

[EDIT2] एक वेब कंटेनर (Grails की तरह), आप classloader में बताने की वहाँ दो विकल्प हैं का उपयोग करते समय:।।

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

मैं यह जानने की उम्मीद कर रहा था कि क्या क्लास.फोरेनाम करने का "ग्रोवी" तरीका था, इस तरह उन्होंने तरीकों पर प्रतिबिंब को इतना आसान बना दिया। मैं आपके उत्तर की सराहना करता हूं और संदेह करता हूं कि यह एकमात्र तरीका हो सकता है। –

+0

@ जॉन: नहीं, यह संभव नहीं है। मैंने ग्रोवी एमएल से पूछा। आपको Class.forName() को कॉल करना होगा क्योंकि "$ name" एक GString और Groovy चर "नाम" की सामग्री के बारे में स्मार्ट होने का प्रयास नहीं करता है। –

+0

@Aaron - धन्यवाद, मैंने ग्रोवी एमएल पर धागा पढ़ा और वह सिर्फ वह जानकारी जो मैं ढूंढ रहा था। –

29

ग्रूवी एमएल पर Guillaume Laforge ने सुझाव दिया है,

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

ही देना होगा परिणाम।

मैं इस कोड के साथ परीक्षण किया है:

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

@chanwit - उदाहरण के लिए धन्यवाद यह बहुत उपयोगी था। –

+1

पहला उदाहरण (कक्षा के रूप में "फू") .get (i) डोमेन स्तर को गतिशील रूप से लोड करने के लिए grails में काम नहीं कर रहा है। –

+3

यह grails में काम करता है 1.3.4 grailsApplication.classLoader.loadClass (नाम); –

2

यहाँ एक और तरीका

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

def target = application.domainClasses.find{it.name == 'ClassName'} 
target.clazz.invokeMethod("Method",args) 
इस पैकेज का नाम निर्दिष्ट करने के लिए की जरूरत नहीं है के साथ

है। सावधान रहें, भले ही आपके पास दो अलग-अलग पैकेजों में समान वर्ग का नाम हो।

+0

मैं Grails 1.2.1 और Groovy 1.6.3 चला रहा हूं, और मुझे क्लासफोरनाम() और उपरोक्त क्लास कास्टिंग विधियों के उपयोग से अपवाद नहीं मिला। यह विशेष दृष्टिकोण मेरे लिए काम किया। –

3

एक उदाहरण के Chanwit के जवाब को दर्शाता हुआ निर्माण करने के लिए एक वृद्धि:

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

Melix ग्रूवी एमएल पर थोड़ी देर वापस गतिशील वर्ग विधि 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() 

आप उदाहरण बनाने के लिए एक और स्ट्रिंग मेटा क्लास विधि बना सकते हैं, उचित के रूप में रीफैक्टरिंग:

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

ग्रोवी शुद्ध मजा है; -)

1

मैं संस्करण 1.8.8 groovy चला रहा हूं ... और सरल उदाहरण काम करता है।

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

Foo."$myFx"(myArg) 

कॉल Foo.myMethodToCall (12) की उम्मीद है और वांछित के रूप में। मुझे नहीं पता कि यह हमेशा मामला रहा है या नहीं।

संबंधित मुद्दे