2010-06-24 16 views
26

मान लीजिए मैं इन इंटरफेस है:का उपयोग पैरामीटर कि कई इंटरफेस पूर्व जेनरिक लागू करता

public interface I1 { 
    void foo(); 
} 

public interface I2 { 
    void bar(); 
} 

और वर्गों:

public class A extends AParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class B extends BParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class C extends CParent implements I1 { 
    // code for foo method here 
} 

अब, जेनरिक के साथ मैं की तरह एक विधि है कर सकते हैं:

public <T extends I1 & I2> void method(T param) { 
    param.foo(); 
    param.bar(); 
} 

और मैं इसे पैरामीटर के रूप में ए और बी दोनों के साथ कॉल कर सकता हूं, लेकिन सी के साथ नहीं (यह I2 लागू नहीं करता है)।

इस प्रकार के प्रकार सुरक्षा पूर्व जेनरिक (जावा < 1.5) प्राप्त करने का कोई तरीका था।

इस बात पर विचार करें कि ए, बी और सी के पास अलग-अलग विरासत पेड़ हैं, और यह वास्तव में एक सामान्य माता-पिता होने वाले माता-पिता और बी-पैरेंट जैसे कुछ करने का विकल्प नहीं है।

मैं जानता हूँ कि तुम कर सकते हो:

public void method(I1 param) { 
    param.foo(); 
    ((I2)param).bar(); 
} 

लेकिन फिर आप भी method(new C()) जो I2 को लागू नहीं करता है कह सकते हैं, तो आप मुसीबत में पड़।

तो क्या ऐसा कोई अन्य तरीका है जिससे आप इसे कर सकते थे?

पीएस : मुझे वास्तव में ऐसा करने की ज़रूरत नहीं है, यह ज्यादातर जिज्ञासा से बाहर है जो मैं पूछता हूं।

उत्तर

20

एक तीसरा इंटरफ़ेस I3 I1 और I2 बढ़ाता है। फिर कक्षा ए और बी दोनों I3 लागू करते हैं, और सामान्य विधि I3 स्वीकार करती है।

शायद यह करने का एकमात्र तरीका है।

+4

मैं इन सबसे छुटकारा इस एहसास हुआ के बाद मैं प्रश्न पूछा है, हालांकि यदि आप एक से अधिक दो इंटरफेस है, और आप मिश्रण और विभिन्न तरीकों के लिए उन से मिलान करना चाहते हैं, यह हो सकता है आप जिस संयोजन का उपयोग करना चाहते हैं उसके लिए एक बाल इंटरफ़ेस बनाना एक दर्द है। मैं 'विधि (I1 और I2 param)' जैसी विधि बनाने के तरीके के साथ और सोच रहा था या पैरामीटर कहने के कुछ अन्य तरीकों से चलने पर कई इंटरफ़ेस लागू होते हैं (कोई नया वर्ग/इंटरफ़ेस बनाये बिना) –

+0

मुझे नहीं लगता डिजाइन दृष्टिकोण में यह अच्छा है। कृपया मेरा जवाब देखें। – hqt

2

जावा 1.5 से पहले आईएमओ संकलन समय पर इस तरह के प्रकार-सेफ्टी प्राप्त करने के लिए कोई समाधान नहीं है। लेकिन "exampleof" का उपयोग कर रनटाइम पर एक आत्मा है।

public void method(Object o) { 
    if (!(o instanceof I1)) 
    throw new RuntimeException("o is not instance of I1"); 

    if (!(o instanceof I2)) 
    throw new RuntimeException("o is not instance of I2"); 

    // go ahead ... 
} 
+0

ठीक है अगर आप प्रश्न में मेरे गैर-सामान्य उदाहरण के लिए 'विधि (नया सी())' कॉल करते हैं, तो आपको 'कक्षा'() 'पर कॉल करने के लिए' I2' पर कास्टिंग करते समय 'क्लासकास्ट अपवाद' प्राप्त होगा, इसलिए इसमें बहुत सुधार नहीं है यहाँ। –

+0

नहीं, आपको पहले RuntimeException मिलेगा, क्योंकि "(o exampleof I2)" की जांच झूठी का मूल्यांकन करती है;) ठीक है मुझे लगता है कि श्री का सुझाव शायद अधिक बेहतर होगा :) क्योंकि आपको जोखिम का जोखिम नहीं मिलेगा रनटाइम अपवाद मेरे सुझाव के साथ क्योंकि संकलक पहले विफल हो जाएगा। – VuuRWerK

2

sri सर्वश्रेष्ठ उत्तर यदि आप ए और बी के हस्ताक्षर को बदलने के लिए हालांकि अनुमति थी है, यदि आपके पास अनुमति नहीं था, तो आप कर सकते थे:

public void method(I1 param1 , I2 param2) { // unpopular classes that do not implement I3 must use this method 
    param1.foo(); 
    param2.bar(); 
} 
public void method(I3 param){ // I hope everybody implements I3 when appropriate 
    param.foo(); 
    param.bar(); 
} 
public void method(A param){// A is a popular class 
    method(param,param); 
} 
public void method(B param){// B is a popular class 
    method(param,param); 
} 
बेशक

, अब जेनरिक का प्रयोग करें।

+0

हां यह काम करेगा, लेकिन यदि I1 तुलनात्मक जैसा कुछ है, I2 Serializable की तरह कुछ है जिसका व्यापक रूप से उपयोग किया जाता है, जिसमें प्रत्येक प्रकार के लिए एक विधि होती है जो कि दोनों को लागू करती है (जो निजी कॉल करती है) एक चुनौती हो सकती है। जब मैंने इस सवाल के बारे में सोचा, तो मैंने अभी ए और बी को उदाहरण के रूप में दिया है, मैं वास्तव में इस बात पर ध्यान नहीं देता कि वे विधि को कॉल करते समय क्या कर रहे हैं (केवल एक चीज जो कि महत्वपूर्ण थी कि वे उन इंटरफेस को लागू करते थे)। और वास्तव में यदि 'विधि() 'एक एपीआई विधि हो सकती है, जो दूसरों के उपयोग के लिए उजागर की जाती है, तो कक्षाओं को कड़ी कोडिंग करना जो इसका उपयोग कर सकता है, यह एक समस्या होगी। –

+0

फिर एक बेहतर विकल्प निजी विधि को सार्वजनिक करना होगा और फिर अधिकांश रुचि के वर्गों के लिए कुछ सुविधाजनक तरीकों को बनाना होगा - जिसमें इंटरफ़ेस श्री के आई 3 इंटरफ़ेस शामिल हो सकते हैं। – emory

6

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

1. मेकअप एक इंटरफेस दोनों दो इंटरफेस लागू होता है:

public interface IC extends IA,IB { 
    // empty method here 
} 

कोड से ऊपर गैर भावना है। आप अन्य इंटरफेस को गठबंधन करने के लिए बस एक अलग इंटरफ़ेस परिभाषित करते हैं, और अंदर कोई नई विधि नहीं है। आप दो इंटरफ़ेस IA और IB को गठबंधन करने के बजाय IC पर कोई मान नहीं जोड़ते हैं। और कुछ परिस्थितियों में, जब आप तीसरे इंटरफेस के लिए उपयुक्त नाम नहीं पा रहे हैं तो इस तरह से आपका कोड "थोड़ा मजेदार" होगा।इस स्थिति में कुछ इंटरफेस नाम जैसे IReadableAndWriteable या ISomethingAndSomethingAndAnotherThing

2. प्रकार विधि के अंदर डाली को बढ़ावा मिलेगा:

public void methodA(IA object) { 
    if (object instance of IB) { 
    ((IB)(object)).someMethod() 
} 

इस तरह बकवास भी है। इनपुट पैरामीटर IA क्यों है, तो आपको इंटरफ़ेस IB से कुछ क्रिया निष्पादित करनी होगी? प्रोग्रामर व्यूपॉइंट के तहत, यह जानने का कोई तरीका नहीं है कि आपके दस्तावेज़ को पढ़ने के अलावा। अन्य लोगों के उपयोग के लिए एक समारोह तैयार करने के लिए यह अच्छा नहीं है।

यह सच है समाधान:

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

आप अपने विधि हस्ताक्षर में दो अलग-अलग मानकों बनाना चाहिए:

public void exampleMethod(IA object1, IB object2) { 
    object1.someMethod() 
    object2.someMethod() 
} 

और विधि ऊपर फोन करने के लिए, आप के अंदर (यदि प्रोग्रामर उपयोग एक ही वस्तु) एक ही पैरामीटर डाल दिया। वे भी विभिन्न वस्तुओं को भी डाल सकते हैं।

public void caller() { 
    IC object3 = new C(); // C is the class implements both IA and IB 
    exampleMethod(object3, object3); 
    IA objectA = new A(); 
    IB objectB = new B(); 
    exampleMethod(objectA, objectB);  
} 

आशा इस मदद :)

+2

क्षमा करें मैं एक पुराना विषय जगाता हूं, लेकिन मुझे लगता है कि अतिरिक्त इसके लायक है। मुझे सच में लगता है कि यह जवाब सबसे अच्छा है।हालांकि, बिंदु 1 के लिए, चाहे यह समझ में आता है या नहीं बल्कि व्यक्तिपरक (यह कम से कम एक अस्थायी समाधान के रूप में समझ सकता है), लेकिन वास्तव में एक उद्देश्य और व्यावहारिक मामला है जो दिखाता है कि यह एक अच्छा डिजाइन नहीं है: यदि आप एक कक्षा को कार्यान्वित करते हैं जो प्रासंगिक इंटरफेस दोनों को बढ़ाता है लेकिन * इंटरफ़ेस जो उन्हें गठबंधन नहीं करता है, तो आप इसे उस विधि में उपयोग नहीं कर सकते जो संयोजन इंटरफ़ेस के लिए पूछता है, हालांकि यह प्रारंभिक अनुबंध का सम्मान करता है। – Matthieu

+1

मैं पिछले जवाब को संपादित नहीं कर सकता, इसलिए मैं एक और जोड़ता हूं: संयुक्त इंटरफ़ेस बनाने का विचार या तो अच्छा या अपूर्ण नहीं है। यह एक मात्र इंटरफेस नहीं होना चाहिए, लेकिन एक रैपर: यदि आप बाहरी पुस्तकालय से अपना कार्यान्वयन प्राप्त करते हैं, लेकिन दो इंटरफेस के किसी भी कार्यान्वयन को प्रयोग करने योग्य बनाना चाहते हैं तो आप lib द्वारा सीमित नहीं होना चाहते हैं, तो आपको आवश्यकता है एक रैपर, या एक एडाप्टर अगर आप पसंद करते हैं। यह समाधान सुनिश्चित करता है कि आपके पास वास्तव में एक ही उदाहरण से जुड़े इंटरफेस दोनों हैं, जबकि उपरोक्त एचटीटी की पोस्ट ऐसी बाधा सुनिश्चित करने की अनुमति नहीं देती है, जो अभी भी लायक हो सकती है। – Matthieu

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