2009-12-12 11 views
17

मैंकक्षा.getMethod() प्रतिबिंब और autoboxing के लिए कोई समाधान?

Class.getMethod(String name, Class... parameterTypes) 

उपयोग करने के लिए विधि मैं दिए गए मापदंडों के साथ लागू करने की जरूरत है पता लगाना चाहते हैं, लेकिन जाहिरा तौर Bug 6176992 में वर्णित के रूप जावा वहाँ autoboxing शामिल नहीं है। तो अगर मेरे परिलक्षित वर्ग एक (स्ट्रिंग, पूर्णांक) हस्ताक्षर के साथ एक विधि है आप अभी भी एक paremeter के रूप में एक {String.class, Integer.class} सरणी के साथ एक NoSuchMethodException मिलता है।

वहाँ इस के लिए किसी भी sollution है? एकमात्र तरीका मैं getMethod() को आदिम और गैर आदिम प्रकारों के हर क्रमपरिवर्तन के साथ कॉल करने के बारे में सोच सकता हूं जिसे मैं वास्तव में नहीं करना चाहता हूं।

संपादित करें: यह और अधिक स्पष्ट करने के लिए: मैं आदिम प्रकार कक्षाओं से अच्छी तरह परिचित हूँ, लेकिन मैं नहीं दिख रहा है कि वे किस तरह मेरी समस्या को हल करने में मदद कर सकता। मेरा पैरामीटर टाइप एरे कहीं से आता है और मुझे पता है कि यह केवल गैर आदिम प्रकारों को वापस कर देगा। मैं कल्पना नहीं कर सकते कि इंटरफेस सिर्फ आदिम प्रकार के साथ घोषित किया जाएगा और कहा कि वास्तव में मेरी समस्या है:

public interface TestInterface() 
{ 
    public void doTest(Integer i1, int i2, double d3, Double d); 
} 

Class<?>[] classes = { Integer.class, Integer.class, Double.class, Double.class } 
// Due to autoboxing I should become the doTest method here, but it doesn't work 
TestInterface.class.getMethod("doTest", classes); 
+0

क्या आप जानते हैं कि विधि का हस्ताक्षर समय से पहले क्या है? – PSpeed

+0

नहीं, वास्तव में नहीं। ठीक है, एक और समाधान उस नाम के साथ सभी विधियों को प्राप्त करना होगा और देखें कि हस्ताक्षर (अन) बॉक्स किए गए प्रकार से मेल खाता है या नहीं। – Daff

उत्तर

11

@Stephen सी का उल्लेख के रूप में, अपने ही उम्मीद खोज अपने आप को करना है। उनकी सभी चेतावनीएं हैं, लेकिन मैं तर्क दूंगा कि थोड़ी लचीलापन ज्यादातर गॉर्च को कवर करने का एक लंबा सफर तय करेगी जब तक कि कॉलर चेतावनी से अवगत थे ... बनाम आपके कॉलर्स हमेशा दर्दनाक विशिष्ट होते हैं।

कोड है कि वास्तव में कुछ इस तरह करता है के लिए तुम यहाँ देख सकते हैं: http://meta-jb.svn.sourceforge.net/viewvc/meta-jb/trunk/dev/src/main/java/org/progeeks/util/MethodIndex.java?revision=3811&view=markup

findMethod() कॉल प्रवेश बिंदु है, लेकिन यह प्रतिनिधियों इस विधि के लिए (कुछ कैशिंग, आदि के बाद):

private Method searchForMethod(String name, Class[] parms) { 
    Method[] methods = type.getMethods(); 
    for(int i = 0; i < methods.length; i++) { 
     // Has to be named the same of course. 
     if(!methods[i].getName().equals(name)) 
      continue; 

     Class[] types = methods[i].getParameterTypes(); 

     // Does it have the same number of arguments that we're looking for. 
     if(types.length != parms.length) 
      continue; 

     // Check for type compatibility 
     if(InspectionUtils.areTypesCompatible(types, parms)) 
      return methods[i]; 
     } 
    return null; 
} 

निरीक्षण -tils.areTypesCompatible() प्रकारों की दो सूचियां लेता है, उनके प्राइमेटिव को सामान्य करता है, और उसके बाद सत्यापित करता है कि एक दूसरे को "असाइन करने योग्य" है। तो यह उस मामले को संभालेगा जहां आपके पास एक इंटीजर है और वह ऐसी विधि को कॉल करने का प्रयास कर रहा है जो int के साथ-साथ उस मामले में जहां स्ट्रिंग है और ऑब्जेक्ट लेने वाली विधि को कॉल करने का प्रयास कर रहे हैं। यह नहीं एक इंट होने और फ्लोट लेने वाली विधि को कॉल करने के मामले को संभालता है। कुछ विशिष्टता होना चाहिए।

एक चेतावनी यह है कि उपर्युक्त विधि केवल विधि क्रम में खोज करती है ताकि अगर अस्पष्टताएं हों तो चयन मनमाने ढंग से होता है। अभ्यास में अब तक मुझे वास्तविक दुनिया के मुद्दे का सामना नहीं हुआ है।

यहाँ संदर्भ के लिए संगतता की जाँच है: सार्वजनिक स्थिर बूलियन areTypesCompatible (कक्षा [] लक्ष्य, कक्षा [] स्रोतों) { अगर (targets.length = सूत्रों!।लंबाई) झूठी वापसी;

for(int i = 0; i < targets.length; i++) { 
     if(sources[i] == null) 
      continue; 

     if(!translateFromPrimitive(targets[i]).isAssignableFrom(sources[i])) 
      return false; 
     } 
    return(true); 
} 

कोड बीएसडी और मेरा है इसलिए स्निपेट का उपयोग करने के लिए कानूनी है। यदि आप तय बजाय यदि आप इस util पैकेज का उपयोग करें सीधे सबसे हाल ही में सार्वजनिक रूप से जारी यहाँ है: https://meta-jb.svn.sourceforge.net/svnroot/meta-jb/trunk/dev/m2-repo/org/meta-jb/meta-jb-util/0.17.1/

और मैं केवल उल्लेख है कि क्योंकि वहाँ एक लंबे समय में एक बंडल डाउनलोड मेरी सक्रिय उपयोगकर्ताओं के अधिकांश के बाद से नहीं किया गया है मेवेन उपयोगकर्ता हैं। मुझे पूर्ण रिलीज काटने से कोड लिखने का अधिक शौक लगता है। ;)

+0

उदाहरण स्रोत के लिए धन्यवाद। मैं कोड आज़माउंगा और आशा करता हूं कि यह बहुत धीमी नहीं होगी। उम्मीद है कि यह त्रुटि प्रवण नहीं है, लेकिन अगर मैं प्रतिबिंबित कक्षाओं में शामिल होने वाले सख्त नियमों के साथ जाऊंगा। – Daff

+0

मिलान परिणामों के इस प्रारंभिक वापसी का एक नकारात्मक पक्ष यह है कि यदि दो मिलान विधियां हैं उदा। (स्ट्रिंग, इंट) और (स्ट्रिंग, इंटीजर) जावा हमेशा एक ही विधि लेते हैं, हालांकि यह निर्धारित करना मुश्किल होगा कि आपकी खोज किस विधि पर वापस आ जाएगी। –

+0

यह वह चेतावनी है जिसका मैंने उल्लेख किया है: "यदि अस्पष्टताएं हैं तो चयन मनमाने ढंग से है।" आम तौर पर यह सुसंगत है लेकिन मैं मानता हूं कि भविष्यवाणी करना मुश्किल है।इसके अलावा, मैं तर्क दूंगा कि यह वैसे भी स्पष्ट नहीं है। जब आप प्रतिबिंब के माध्यम से कॉल कर रहे होते हैं तो आप हमेशा "इंटीजर" पास कर रहे होते हैं, भले ही आपने "int" आधारित विधि की तलाश शुरू की हो। अच्छी बात यह है कि अगर केवल "int" या "इंटीजर" होता है तो आप किसी भी तरह से कवर होते हैं। जिन मामलों में आपके पास है (स्ट्रिंग, int) और (स्ट्रिंग, इंटीजर) बहुत दुर्लभ हैं ... दुर्लभ अभी भी कोई भी दूसरे को कॉल नहीं करता है। – PSpeed

2

Integer.class पूर्णांक ऑब्जेक्ट प्रकार का प्रतिनिधित्व करता है। Integer.TYPE int primitive प्रकार का प्रतिनिधित्व करता है। क्या वह काम करता है?

+0

वैसे यह वास्तव में समस्या है। getClass()। getMethod (स्ट्रिंग नाम, कक्षा ... पैरामीटर टाइप) पूरे ऑटोबॉक्सिंग चीज़ की परवाह नहीं करता है, इसलिए वर्तमान में कोई तरीका नहीं ढूंढने का तरीका प्रतीत होता है जहां पैरामीटर (un) बॉक्स किए जा सकते हैं। मैं न तो विधि हस्ताक्षर जानता हूं और न ही getMethod के लिए क्लास सरणी पैरामीटर कैसा दिखता है। – Daff

+0

कोई पूछेगा कि आप एक विधि क्यों बुला रहे हैं जहां आप पैरामीटर नहीं जानते ... यदि आप वर्णन करते हैं कि आप थोड़ा और क्या कर रहे हैं तो शायद एक बेहतर समाधान स्वयं उपस्थित होगा। – TofuBeer

+0

मान लीजिए कि आप सही हैं, मैंने अपना प्रश्न अपडेट किया। एक विधि को कॉल करना जहां मुझे पैरामीटर नहीं पता है, एक सामान्य प्रेषण तंत्र का हिस्सा है। – Daff

4

हाँ आपको Integer.TYPE या (समतुल्य) int.class का उपयोग करने की आवश्यकता है।

अद्यतन: "मेरे parameterTypes सरणी कहीं से आता है और मुझे पता है कि यह केवल गैर आदिम प्रकार वापस आ जाएगी।" खैर, तो यह "कहीं" समस्या है। यदि वे आपको इच्छित विधि के उचित हस्ताक्षर नहीं देते हैं, तो आप इसे कैसे ढूंढेंगे? क्या होगा यदि दो ओवरलोडेड विधियां हैं, जो कि उनमें से केवल एक में एक आदिम होती है और दूसरा रैपर वर्ग लेता है? तब यह कौन सा चुनता है? मेरा मतलब है, मुझे लगता है कि तुम सिर्फ सभी तरीकों के माध्यम से लूप सकता है यदि आप वास्तव में कोई विकल्प नहीं तो है, और सही नाम के साथ एक के लिए देखो, और मैन्युअल रूप से है कि क्या वे सही हैं, या आदिम समकक्ष हैं के लिए सभी पैरामीटर प्रकार की जाँच करें।

+0

मैंने अपना प्रश्न अपडेट किया, शायद यह थोड़ा स्पष्ट हो जाता है कि आदिम प्रकार के वर्ग मेरी मदद क्यों नहीं करते हैं। – Daff

2

प्रतिबिंब विधि स्वरूप को प्रभावी संकलक के रूप में के रूप में परिष्कृत नहीं है। मैं समझता हूं कि आपको ऐसा कुछ क्यों चाहिए। लगता है कि रनटाइम पर आपके पास एक विधि का नाम है, और ऑब्जेक्ट्स की एक सरणी तर्क के रूप में है, और आप तर्कों के प्रकारों के आधार पर आपको सटीक विधि देने के प्रतिबिंब चाहते हैं, लेकिन इससे अधिक जटिल है। उदाहरण के लिए:

void f(Integer i){..} 
void f(int i){...} 

जब तर्क प्रकार इंटीजर है, तो आप कौन से चुन सकते हैं? एक और भी जटिल काम एक:

void f(Integer i, List l){...} 
void f(Object o, LinkedList l){...} 

संकलक नियम "सबसे विशिष्ट विधि" स्थैतिक जानकारी के आधार पर चयन करने के लिए का एक सेट है, यदि यह निर्धारित नहीं कर सकता है कि यह आपको तुरंत सतर्क करेगा।

आपको "सबसे विशिष्ट विधि" को खोजने के लिए कंपाइलर अनुकरण करना और एल्गोरिदम लिखना है। (ओह, और ऑटो-मुक्केबाजी के विचार के साथ, लगभग भूल गया!)

1

इस समय एकमात्र उत्तर जावा कंपाइलर के प्रकार के प्रचार नियमों को अनुकरण करने के लिए कोड लिखना है, जो प्रतिबिंबित रूप से सबसे उपयुक्त विधि चुनते हैं। ऑटोबॉक्सिंग और अनबॉक्सिंग केवल प्रकार के प्रचार के उदाहरण हैं जो संकलक के बारे में जानते हैं ...

जावा प्रतिबिंबित एपीआई पहले से ही ऐसा क्यों नहीं करते हैं? मैं कई कारणों से सोच सकता हूं।

  • जावा 1 से पहले।5, getMethod कक्षा और दोस्तों को यह समझ में नहीं आया कि कैसे करना है (उदाहरण के लिए) int से float का प्रचार। अगर पूर्व-1.5 की आवश्यकता नहीं थी, तो अब क्यों?

  • जोड़ना सामान इस तरह का भी धीमी बुला चिंतनशील विधि कर देगा।

  • autoboxing और unboxing गैर चिंतनशील विधि मंगलाचरण के साथ भ्रमित कर सकते हैं। प्रतिबिंब जोड़ना केवल और भ्रम जोड़ देगा।

  • पदोन्नति नियमों के रनटाइम कार्यान्वयन में नए रनटाइम त्रुटि मामलों की एक श्रेणी शामिल होगी जिसे अपवादों के लिए मैप किया जाना चाहिए और उपयोगकर्ता कोड द्वारा निदान किया जाना चाहिए। ये विशेष रूप से मुश्किल होंगे। उदाहरण के लिए, इसे एक अस्पष्ट विधि कॉल के प्रतिबिंबित समकक्ष से निपटना होगा।

  • पश्च संगतता के लिए व्यापक आवश्यकता मतलब यह है कि सूर्य नए तरीकों के रूप में इस लागू करने के लिए होगा। वे मौजूदा तरीकों के व्यवहार को नहीं बदल सकते हैं क्योंकि इससे संभावित रूप से हजारों ग्राहकों के मौजूदा अनुप्रयोगों को तोड़ दिया जाएगा।

एक अंतिम बिंदु है जो विशेष रूप से ओपी के उपयोग-मामले (वर्णित) से संबंधित है। ओ पी का कहना है कि अपने कोड (उदाहरण के लिए) क्या उम्मीद करने के पता नहीं है लक्ष्य वर्ग पर एक int या Integer पैरामीटर के साथ एक विधि। मान लीजिए कि जिस व्यक्ति ने लक्ष्य वर्ग लिखा है, दोनों ओवरलोड ... और अर्थशास्त्र उपरोक्त (या अनिश्चितता से) अलग हैं? इससे कोई फर्क नहीं पड़ता कि आप क्या करते हैं, ऐसी स्थितियां होंगी जहां ओपी का कोड अधिभार को चुनता है जिसे ग्राहक उम्मीद नहीं करता है। खराब।

IMO, यह कुछ सरल एपीआई नियमों का कहना है कि जब वह रैपर बनाम पुरातन उपयोग करने के लिए सही है लागू करने के लिए ओ पी के कोड के लिए बेहतर है।

+0

धन्यवाद, इसके बारे में सोचना, जो वास्तव में समझ में आता है। हो सकता है कि मुझे सिर्फ नियम बनाना चाहिए कि प्रतिबिंबित वर्गों में केवल लिपटे प्रकार हो सकते हैं यदि वे कॉल करना चाहते हैं। – Daff

0

आप अपने प्रतिबिंब कॉल के आस-पास कुछ अतिरिक्त तर्क जोड़ सकते हैं जो आपके Integer.class (या जो कुछ भी) को इसी आदिम वर्ग में परिवर्तित करने का प्रयास करता है, और फिर जब तक आप मैच प्राप्त नहीं करते हैं तब तक विधि को देखकर। यदि आपके पास अपाचे कॉमन्स लैंग है, तो wrapperToPrimitive विधि आपके लिए यह वार्तालाप करेगी, लेकिन इसे स्वयं लिखना तुच्छ है।

  1. रूप getMethod प्रदर्शन करना हमेशा की तरह
  2. तो कुछ भी नहीं मिला है, तो किसी भी पैरामीटर प्रकार जो इसी पुरातन
  3. उन के प्रत्येक संयोजन के लिए है के लिए देखो, कुछ चिपक जब तक एक और देखने प्रदर्शन करते हैं।

सुरुचिपूर्ण नहीं है, और बहुत से आदिम मानकों के साथ विधियों के लिए, यह धीमा भी हो सकता है।

9

यदि आपके पास संस्करण> = 2.5 के साथ कॉमन्स-लैंग है, तो आप MethodUtils.getMatchingAccessibleMethod (...) का उपयोग कर सकते हैं जो मुक्केबाजी प्रकार के मुद्दों को संभाल सकता है।

+0

आकर्षण की तरह काम करता है। – rlegendi

0

, अगर यह एक आदिम प्रकार निर्धारित करने के Class.isPrimitive() उपयोग करने का प्रयास तो अगर यह होता है, प्रतिबिंब का उपयोग TYPE क्षेत्र निकालते हैं और है कि अगर बराबर है की जाँच करने के। तो, बहुत उदार छद्म कोड में:

for(Method m:getDeclaredMethods()) 
    for(Class c:m.getParameterTypes() && Class desired:desiredMethodArgTypes) 
    if(c.isAssignableFrom(desired)) 
     //matches 
    if(c.isPrimitive() && c==desired.getDeclaredField("TYPE").get(desiredObject)) 
     //matches 
+0

अन्य खबरों में, मैं डिस्लेक्सिक हूं - सालों से (इसे ऊपर से हाथ तक टाइप करने तक) मैंने सोचा है कि यह 'AssignableForm() 'था –

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