जावा - इंटरफ़ेस कार्यान्वयन में विधि का नाम टक्कर


88

यदि मेरे पास दो इंटरफेस हैं, तो दोनों अपने उद्देश्यों में काफी भिन्न हैं, लेकिन एक ही विधि हस्ताक्षर के साथ, मैं कैसे एक वर्ग को लागू करने के लिए मजबूर कर सकता हूं बिना एक भी विधि लिखने के लिए जो दोनों इंटरफेस के लिए कार्य करता है और विधि में कुछ जटिल तर्क लिखता है कार्यान्वयन जो जाँचता है कि किस प्रकार की वस्तु को कॉल किया जा रहा है और उचित कोड का आह्वान करें?

C # में, इसे स्पष्ट इंटरफ़ेस कार्यान्वयन के रूप में कहा जाता है। क्या जावा में कोई समान तरीका है?


37
जब एक वर्ग को एक ही हस्ताक्षर के साथ दो तरीकों को लागू करना होता है जो अलग-अलग चीजें करते हैं , तो आपका वर्ग लगभग निश्चित रूप से बहुत सारी चीजें कर रहा है।
जोकिम सॉयर

15
उपरोक्त हमेशा IMO.Sometimes सच नहीं हो सकता है, एक एकल वर्ग में, आपको ऐसे तरीकों की आवश्यकता होती है जो एक बाहरी अनुबंध (इस प्रकार हस्ताक्षर पर विवश) की पुष्टि करें, लेकिन जिनके पास अलग-अलग कार्यान्वयन हैं। वास्तव में, गैर-तुच्छ वर्ग को डिजाइन करते समय ये सामान्य आवश्यकताएं हैं। ओवरलोडिंग और ओवरराइडिंग जरूरी तरीके हैं जो उन तरीकों की अनुमति देने के लिए तंत्र हैं जो अलग-अलग चीजें करते हैं जो हस्ताक्षर में भिन्न नहीं हो सकते हैं, या बहुत थोड़े से अलग हो सकते हैं। मेरे पास यहां बस थोड़ा अधिक प्रतिबंध है कि यह सबक्लासिंग की अनुमति नहीं देता है / और यहां तक ​​कि अनुमति भी नहीं देता है हस्ताक्षर पर थोड़ी भिन्नता।
भास्कर

1
मुझे यह जानने में कठिनाई होगी कि ये वर्ग और विधियाँ क्या हैं।
उरी

2
मुझे ऐसे मामले का सामना करना पड़ा जहां एक विरासत "पता" वर्ग ने व्यक्ति और फर्म इंटरफेस को लागू किया, जिसमें गेटनेम () पद्धति थी, जो डेटा मॉडल से केवल एक स्ट्रिंग लौटाता था। व्यवसाय की एक नई आवश्यकता ने निर्दिष्ट किया कि पर्सनगेटनाम () एक स्ट्रिंग को "उपनाम, दिए गए नामों" के रूप में स्वरूपित करता है। बहुत चर्चा के बाद, डेटाबेस के बजाय डेटा को फिर से तैयार किया गया।
Belwood

12
बस यह बताते हुए कि कक्षा लगभग निश्चित रूप से बहुत सारी चीजें कर रही है, नहीं है। मुझे अभी यह बहुत मामला मिला है कि मेरी कक्षा में 2 अलग-अलग इंटरफेस से mehod नाम टकराव हैं, और मेरी कक्षा बहुत सारी चीजें नहीं कर रही है। उद्देश्य काफी समान हैं, लेकिन थोड़ा अलग काम करते हैं। खराब सॉफ़्टवेयर डिज़ाइन को लागू करने के प्रश्नकर्ता पर आरोप लगाकर स्पष्ट रूप से गंभीर रूप से विकलांग प्रोग्रामिंग भाषा का बचाव करने का प्रयास न करें!
j00hi

जवाबों:


75

नहीं, जावा में एक कक्षा में दो अलग-अलग तरीकों से एक ही तरीके को लागू करने का कोई तरीका नहीं है।

यह कई भ्रामक स्थितियों को जन्म दे सकता है, यही वजह है कि जावा ने इसे अस्वीकार कर दिया है।

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

class Impl implements ISomething, ISomething2 {
   void doSomething() {} // There can only be one implementation of this method.
}

आप क्या कर सकते हैं दो वर्गों में से एक वर्ग की रचना करें जो प्रत्येक एक अलग इंटरफ़ेस को लागू करता है। तब उस एक वर्ग में दोनों इंटरफेस का व्यवहार होगा।

class CompositeClass {
    ISomething class1;
    ISomething2 class2;
    void doSomething1(){class1.doSomething();}
    void doSomething2(){class2.doSomething();}
}

9
लेकिन इस तरह, मैं एक सम्मिश्र का एक उदाहरण पारित नहीं कर सकता कहीं इंटरफेस के संदर्भ (ISomething या ISomething2) की उम्मीद है? मैं ग्राहक कोड से यह भी उम्मीद नहीं कर सकता कि वह मेरे उदाहरण को उपयुक्त इंटरफ़ेस में डाल सके, इसलिए क्या मैं इस प्रतिबंध से कुछ नहीं खो रहा हूं? इस बात पर भी ध्यान दें कि वर्गों को लिखते समय जो वास्तव में संबंधित इंटरफेस को लागू करते हैं, हम कोड को एक एकल वर्ग में रखने का लाभ ढीला कर देते हैं, जो कभी-कभी एक गंभीर बाधा हो सकती है।
भास्कर

9
@ भास्कर, आप मान्य बिंदु बनाते हैं। मेरे पास सबसे अच्छी सलाह है कि आप उस कक्षा में एक ISomething1 CompositeClass.asInterface1();और ISomething2 CompositeClass.asInterface2();तरीका जोड़ दें । तब आप केवल एक या दूसरे को समग्र वर्ग से बाहर निकाल सकते हैं। हालांकि इस समस्या का कोई बड़ा हल नहीं है।
9

1
भ्रामक स्थितियों के बारे में बात करने से यह हो सकता है, क्या आप एक उदाहरण दे सकते हैं? क्या हम एक अतिरिक्त गुंजाइश रिज़ॉल्यूशन के रूप में विधि के नाम के साथ जोड़े गए इंटरफ़ेस नाम के बारे में नहीं सोच सकते हैं जो फिर टकराव / भ्रम से बच सकते हैं?
भास्कर

@ भास्कर अगर हमारी कक्षाएं एकल जिम्मेदारी सिद्धांत का पालन करें तो बेहतर है। अगर एक वर्ग मौजूद है जो दो बहुत अलग इंटरफेस को लागू करता है तो मुझे लगता है कि एकल जिम्मेदारी का ख्याल रखने के लिए कक्षाओं को विभाजित करने के लिए डिजाइन को फिर से तैयार किया जाना चाहिए।
अनिरुद्धन J

1
यह कितना भ्रामक होगा, वास्तव में, कुछ ऐसा करने की अनुमति देने के लिए public long getCountAsLong() implements interface2.getCount {...}[मामले में इंटरफ़ेस की आवश्यकता होती है, longलेकिन वर्ग के उपयोगकर्ताओं को अपेक्षा होती है int] या private void AddStub(T newObj) implements coolectionInterface.Add[ collectionInterfaceएक canAdd()विधि है, और इस वर्ग के सभी उदाहरणों के लिए यह वापस आ जाता है false]?
सुपरकैट

13

जावा में इसे हल करने का कोई वास्तविक तरीका नहीं है। आप आंतरिक कक्षाओं को वर्कअराउंड के रूप में उपयोग कर सकते हैं:

interface Alfa { void m(); }
interface Beta { void m(); }
class AlfaBeta implements Alfa {
    private int value;
    public void m() { ++value; } // Alfa.m()
    public Beta asBeta() {
        return new Beta(){
            public void m() { --value; } // Beta.m()
        };
    }
}

यद्यपि यह जातियों के लिए अनुमति नहीं देता AlfaBetaहै Beta, लेकिन डाउनकास्ट आम तौर पर बुराई है, और अगर यह उम्मीद की जा सकती है कि एक Alfaउदाहरण में अक्सर एक Betaपहलू भी होता है, और किसी कारण से (आमतौर पर अनुकूलन एकमात्र वैध कारण होता है) आप सक्षम होना चाहते हैं यह परिवर्तित करने के लिए करने के लिए Beta, आप की एक उप-इंटरफ़ेस बना सकता है Alfaके साथ Beta asBeta()उस में।


क्या आपको आंतरिक वर्ग के बजाय अनाम वर्ग से मतलब है?
जैद मसऊद

2
@ZaidMasud का अर्थ है मैं आंतरिक वर्ग, क्योंकि वे निजी वस्तु की निजी स्थिति तक पहुँच सकते हैं)। ये आंतरिक वर्ग बेशक गुमनाम भी हो सकते हैं।
gustafc

11

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

उत्तरार्द्ध मामले के लिए एक ठोस उदाहरण देने के लिए, मान लीजिए कि आप दोनों को लागू करना चाहते हैं Collectionऔर MyCollection(जिसमें Collectionकोई अंतर नहीं है और एक असंगत इंटरफ़ेस है)। आप एक Collection getCollectionView()और MyCollection getMyCollectionView()कार्य प्रदान कर सकते हैं जो एक ही अंतर्निहित डेटा का उपयोग करते हुए, एक हल्के वजन का कार्यान्वयन प्रदान करते हैं Collectionऔर MyCollection

पूर्व मामले के लिए ... मान लीजिए कि आप वास्तव में पूर्णांकों की एक श्रृंखला और तार की एक सरणी चाहते हैं। दोनों से इनहेरिट करने के बजाय List<Integer>और List<String>, आप प्रकार के एक सदस्य होना चाहिए List<Integer>और प्रकार के एक अन्य सदस्य List<String>हैं, और उन सदस्यों के लिए दोनों से प्राप्त करना नहीं बल्कि कोशिश की तुलना में, देखें। यहां तक ​​कि अगर आपको केवल पूर्णांक की एक सूची की आवश्यकता है, तो इस मामले में विरासत पर रचना / प्रतिनिधिमंडल का उपयोग करना बेहतर है।


मुझे ऐसा नहीं लगता। आप उन पुस्तकालयों को भूल रहे हैं जिनके लिए आपको उनके साथ संगत होने के लिए विभिन्न इंटरफेस को लागू करने की आवश्यकता होती है। आप कई बार परस्पर विरोधी पुस्तकालयों का उपयोग करके इसे चला सकते हैं और फिर आप इसे अपने कोड में चला सकते हैं।
नाइटपूल

1
@ नाइटपूल यदि आप कई पुस्तकालयों का उपयोग करते हैं, जिनमें से प्रत्येक को अलग-अलग इंटरफेस की आवश्यकता होती है, तब भी दोनों इंटरफेस को लागू करने के लिए एक ही वस्तु के लिए आवश्यक नहीं है; आपके पास ऑब्जेक्ट में दो अलग-अलग इंटरफेस को वापस करने के लिए एक्सेसर्स हो सकते हैं (और अंतर्निहित लाइब्रेरी में से किसी एक पर ऑब्जेक्ट को पास करते समय उपयुक्त एक्सेसर को कॉल करें)।
माइकल आरोन सफ़्यान

1

"शास्त्रीय" जावा समस्या मेरे Android विकास को भी प्रभावित करती है ...
इसका कारण सरल प्रतीत होता है:
आपके द्वारा उपयोग की जाने वाली अधिक चौखटें / पुस्तकालय, अधिक आसानी से चीजें नियंत्रण से बाहर हो सकती हैं ...

मेरे मामले में, मेरे पास एक BootStrapperApp वर्ग है android.app.Application से विरासत में मिला है ,
जबकि एक ही वर्ग को एकीकृत करने के लिए MVVM फ्रेमवर्क के प्लेटफ़ॉर्म इंटरफ़ेस को भी लागू करना चाहिए ।
विधि टकराव एक गेटस्ट्रिंग () विधि पर हुआ , जो दोनों इंटरफेस द्वारा घोषित किया गया है और अलग-अलग संदर्भों में अलग-अलग कार्यान्वयन होना चाहिए।
वर्कअराउंड (बदसूरत..आईएमओ) सभी प्लेटफॉर्म को लागू करने के लिए एक आंतरिक वर्ग का उपयोग कर रहा हैतरीकों, सिर्फ एक मामूली विधि हस्ताक्षर संघर्ष के कारण ... कुछ मामलों में, ऐसी उधार विधि का उपयोग बिल्कुल भी नहीं किया जाता है (लेकिन प्रभावित प्रमुख डिजाइन शब्दार्थ)।
मैं सहमत हूँ कि C # -स्टाइल स्पष्ट संदर्भ / नाम स्थान संकेत उपयोगी है।


1
मैंने कभी नहीं महसूस किया कि जब तक मैंने एंड्रॉइड विकास के लिए जावा का उपयोग शुरू नहीं किया, तब तक सी # विचारशील और सुविधा संपन्न था। मैंने उन C # सुविधाओं को लिया। जावा में बहुत अधिक सुविधाओं का अभाव है।
डेमन वेजीटेबल्स

1

मेरे दिमाग में आया एकमात्र समाधान रेफरी वस्तुओं का उपयोग कर रहा है, जिसे आप मुलर चेतावनियों को निहित करना चाहते हैं।

उदाहरण: आप को लागू करने के लिए 2 इंटरफेस हैं

public interface Framework1Interface {

    void method(Object o);
}

तथा

public interface Framework2Interface {
    void method(Object o);
}

आप उन्हें दो मुख् य वस्तुओं में संलग्न कर सकते हैं:

public class Facador1 implements Framework1Interface {

    private final ObjectToUse reference;

    public static Framework1Interface Create(ObjectToUse ref) {
        return new Facador1(ref);
    }

    private Facador1(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework1Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork1(o);
    }
}

तथा

public class Facador2 implements Framework2Interface {

    private final ObjectToUse reference;

    public static Framework2Interface Create(ObjectToUse ref) {
        return new Facador2(ref);
    }

    private Facador2(ObjectToUse refObject) {
        this.reference = refObject;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Framework2Interface) {
            return this == obj;
        } else if (obj instanceof ObjectToUse) {
            return reference == obj;
        }
        return super.equals(obj);
    }

    @Override
    public void method(Object o) {
        reference.methodForFrameWork2(o);
    }
}

अंत में आपको जो वर्ग चाहिए वह कुछ ऐसा होना चाहिए

public class ObjectToUse {

    private Framework1Interface facFramework1Interface;
    private Framework2Interface facFramework2Interface;

    public ObjectToUse() {
    }

    public Framework1Interface getAsFramework1Interface() {
        if (facFramework1Interface == null) {
            facFramework1Interface = Facador1.Create(this);
        }
        return facFramework1Interface;
    }

    public Framework2Interface getAsFramework2Interface() {
        if (facFramework2Interface == null) {
            facFramework2Interface = Facador2.Create(this);
        }
        return facFramework2Interface;
    }

    public void methodForFrameWork1(Object o) {
    }

    public void methodForFrameWork2(Object o) {
    }
}

अब आप अपनी कक्षा को "बेनकाब" करने के लिए गेट्स * के तरीकों का उपयोग कर सकते हैं


0

इन कामों को करने के लिए आप एडॉप्टर पैटर्न का उपयोग कर सकते हैं। प्रत्येक इंटरफ़ेस के लिए दो एडेप्टर बनाएं और उसका उपयोग करें। यह समस्या को हल करना चाहिए।


-1

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

public class MyClass{

    private String name;

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

अब आपको इसे शेल्फ में पास करने की आवश्यकता है WizzBangProcessor जिसे WBPInterface को लागू करने के लिए कक्षाओं की आवश्यकता होती है ... जिसमें गेटनेम () विधि भी है, लेकिन आपके ठोस कार्यान्वयन के बजाय, यह इंटरफ़ेस एक प्रकार का नाम वापस करने की विधि की अपेक्षा करता है। Wizz बैंग प्रसंस्करण की।

C # में यह एक ट्रिवियल होगा

public class MyClass : WBPInterface{

    private String name;

    String WBPInterface.getName(){
        return "MyWizzBangProcessor";
    }

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

जावा टफ में आपको मौजूदा तैनात कोड बेस में हर बिंदु की पहचान करनी होगी, जहां आपको एक इंटरफेस से दूसरे में बदलने की जरूरत है। यकीन है कि WizzBangProcessor कंपनी को getWizzBangProcessName () का उपयोग करना चाहिए था, लेकिन वे डेवलपर्स भी हैं। उनके संदर्भ में getName ठीक था। दरअसल, जावा के बाहर, अधिकांश अन्य OO आधारित भाषाएं इसका समर्थन करती हैं। जावा सभी तरीकों को एक ही विधि NAME के ​​साथ लागू करने के लिए मजबूर करने में दुर्लभ है।

अधिकांश अन्य भाषाओं में एक कंपाइलर होता है जो "इस वर्ग में इस पद्धति जो इस कार्यान्वित इंटरफ़ेस में इस पद्धति के हस्ताक्षर से मेल खाता है, इसे लागू करना है" कहने के लिए एक निर्देश लेने से अधिक खुशी है। अंतरापृष्ठों को परिभाषित करने के पूरे बिंदु के बाद परिभाषा को क्रियान्वयन से अलग करने की अनुमति है। (जावा में इंटरफेस में डिफ़ॉल्ट तरीके होने पर भी मुझे शुरू न करें, अकेले डिफ़ॉल्ट को ओवरराइड करने दें ... क्योंकि निश्चित रूप से, सड़क कार के लिए डिज़ाइन किए गए प्रत्येक घटक को उड़ान कार में पटक दिया जाना चाहिए और बस काम करना चाहिए - हे वे दोनों कार हैं ... मुझे यकीन है कि डिफ़ॉल्ट कार्यक्षमता का कहना है कि आपकी संतृप्त नौसेना डिफ़ॉल्ट पिच और रोल इनपुट से प्रभावित नहीं होगी, क्योंकि कारें केवल जम्हाई लेती हैं!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.