एक ही विधि के साथ एक कक्षा में दो इंटरफेस को लागू करना। किस इंटरफ़ेस विधि को ओवरराइड किया जाता है?


235

एक ही विधि के नाम और हस्ताक्षर के साथ दो इंटरफेस। लेकिन एक ही वर्ग द्वारा कार्यान्वित किया जाता है फिर कंपाइलर किस विधि की पहचान करेगा कि किस इंटरफ़ेस के लिए है?

उदाहरण के लिए:

interface A{
  int f();
}

interface B{
  int f();
}

class Test implements A, B{   
  public static void main(String... args) throws Exception{   

  }

  @Override
  public int f() {  // from which interface A or B
    return 0;
  }
}   

जवाबों:


337

यदि एक प्रकार दो इंटरफेस को लागू करता है, और प्रत्येक interfaceएक ऐसी विधि को परिभाषित करता है जिसमें समान हस्ताक्षर होते हैं, तो वास्तव में एक ही विधि है, और वे अलग-अलग नहीं हैं। यदि कहें, दो तरीकों में परस्पर विरोधी रिटर्न प्रकार हैं, तो यह एक संकलन त्रुटि होगी। यह वंशानुक्रम, विधि के ओवरराइड करने, छिपाने और घोषणाओं का सामान्य नियम है, और यह केवल 2 विरासत वाले interfaceतरीकों के बीच संभावित संघर्षों पर भी लागू होता है , लेकिन यह भी एक interfaceऔर एक सुपर classविधि, या यहां तक ​​कि जेनेरिक के प्रकार के कारण संघर्ष भी है।


संगतता उदाहरण

यहां एक उदाहरण है जहां आपके पास एक है interface Gift, जिसमें एक present()विधि है (जैसे, उपहार पेश करना), और एक भी interface Guest, जिसमें एक present()विधि भी है (जैसा कि, अतिथि मौजूद है और अनुपस्थित नहीं है)।

Presentable johnnya Giftऔर a दोनों है Guest

public class InterfaceTest {
    interface Gift  { void present(); }
    interface Guest { void present(); }

    interface Presentable extends Gift, Guest { }

    public static void main(String[] args) {
        Presentable johnny = new Presentable() {
            @Override public void present() {
                System.out.println("Heeeereee's Johnny!!!");
            }
        };
        johnny.present();                     // "Heeeereee's Johnny!!!"

        ((Gift) johnny).present();            // "Heeeereee's Johnny!!!"
        ((Guest) johnny).present();           // "Heeeereee's Johnny!!!"

        Gift johnnyAsGift = (Gift) johnny;
        johnnyAsGift.present();               // "Heeeereee's Johnny!!!"

        Guest johnnyAsGuest = (Guest) johnny;
        johnnyAsGuest.present();              // "Heeeereee's Johnny!!!"
    }
}

उपरोक्त स्निपेट संकलन और चलाता है।

ध्यान दें कि केवल एक ही @Override आवश्यक है !!! । इसका कारण यह है Gift.present()और Guest.present()" @Overrideअसमान" ( JLS 8.4.2 ) हैं।

इस प्रकार, johnny केवल एक ही कार्यान्वयन है present(), और इससे कोई फर्क नहीं पड़ता कि आप कैसे व्यवहार करते हैं johnny, चाहे एक Giftया एक के रूप में Guest, एक ही तरीका है।


असंगति उदाहरण

यहाँ एक उदाहरण है जहाँ दो विरासत में मिली विधियाँ @Overrideअसमान नहीं हैं :

public class InterfaceTest {
    interface Gift  { void present(); }
    interface Guest { boolean present(); }

    interface Presentable extends Gift, Guest { } // DOES NOT COMPILE!!!
    // "types InterfaceTest.Guest and InterfaceTest.Gift are incompatible;
    //  both define present(), but with unrelated return types"
}

यह आगे दोहराता है कि सदस्यों को विरासत में प्राप्त होने interfaceवाले सदस्य घोषणाओं के सामान्य नियम का पालन करना चाहिए। यहां हमारे पास असंगत रिटर्न प्रकारों के साथ हैं Giftऔर Guestपरिभाषित present()करते हैं: एक voidदूसरे के साथ boolean। उसी कारण से कि आप एक void present()और boolean present()एक प्रकार में नहीं कर सकते हैं , इस उदाहरण के परिणामस्वरूप संकलन त्रुटि होती है।


सारांश

आप उन तरीकों को विरासत में दे सकते हैं @Override, जो असमान हैं, विधि की सामान्य आवश्यकताओं के अधीन होते हैं जो ओवरराइडिंग और छुपाते हैं। क्योंकि वे हैं @Override -equivalent, प्रभावी रूप से वहाँ लागू करने के लिए केवल एक ही तरीका है, और इस तरह वहाँ भेद / से चयन करने के लिए कुछ भी नहीं है।

कंपाइलर को यह पता लगाने की आवश्यकता नहीं है कि कौन सी विधि किस इंटरफ़ेस के लिए है, क्योंकि एक बार जब वे निर्धारित होने के लिए @Override-सामान्य होते हैं, तो वे एक ही विधि होते हैं।

संभावित असंगतताओं को हल करना एक मुश्किल काम हो सकता है, लेकिन यह पूरी तरह से एक और मुद्दा है।

संदर्भ


धन्यवाद - यह मददगार था। हालांकि, मेरे पास असंगतता पर एक और सवाल था, जिसे मैंने एक नए सवाल के
11:47 पर जुलाब

2
BTW यह defaultजावा 8 में विधियों के समर्थन से थोड़ा बदल जाता है
पीटर लॉरी

संभावित असंगतताओं को हल करने के लिए समग्र कक्षाएं चाल हो सकती हैं :), लेकिन, मुझे ऐसी समस्या कभी नहीं हुई थी, और अभी भी यह स्पष्ट है कि ऐसा हो सकता है।
कुंभ राशि

1
यह लेख प्रस्तुत करता है एक डिजाइन पैटर्न है कि करने के लिए इस्तेमाल किया जा सकता कुछ हद तक स्थिति है जहाँ आप इंटरफ़ेस टकराने दो लागू करने की आवश्यकता के साथ सौदा, कहते हैं Fooऔर Bar। मूल रूप से आपके पास अपनी कक्षा को इंटरफेस में से एक को लागू करने, कहने Fooऔर Bar asBar()आंतरिक Barइंटरफ़ेस को वापस करने के लिए एक विधि प्रदान करता है जो दूसरे इंटरफ़ेस को लागू करता है। सही नहीं है क्योंकि आपकी कक्षा अंततः "एक बार" नहीं है, लेकिन यह कुछ परिस्थितियों में उपयोगी हो सकता है।
जवाहरू

1
im a java developer लेकिन c # वास्तव में इस पर अधिक चतुर है: stackoverflow.com/questions/2371178/…
आमिर ज़ियारती

25

इसे इस प्रश्न के डुप्लिकेट के रूप में चिह्नित किया गया था /programming/24401064/understanding-and-solve-the-diamond-problems-in-java

आपको एक बहु विरासत समस्या प्राप्त करने के लिए जावा 8 की आवश्यकता है, लेकिन यह अभी भी इस तरह से एक diamon समस्या नहीं है।

interface A {
    default void hi() { System.out.println("A"); }
}

interface B {
    default void hi() { System.out.println("B"); }
}

class AB implements A, B { // won't compile
}

new AB().hi(); // won't compile.

जेबी निज़ेट की टिप्पणियों के रूप में आप इसे मेरे ओवरराइडिंग को ठीक कर सकते हैं।

class AB implements A, B {
    public void hi() { A.super.hi(); }
}

हालाँकि, आपको इससे कोई समस्या नहीं है

interface D extends A { }

interface E extends A { }

interface F extends A {
    default void hi() { System.out.println("F"); }
}

class DE implement D, E { }

new DE().hi(); // prints A

class DEF implement D, E, F { }

new DEF().hi(); // prints F as it is closer in the heirarchy than A.

वाह। यह मेरे लिए नया है। उन्हें java 8 में डिफ़ॉल्ट क्यों बनाना पड़ा?
एरन मुराद

1
60% कोडबेस को तोड़ने के बिना इंटरफेस (विशेष रूप से संग्रह इंटरफेस) में नए तरीकों को जोड़ने की सुविधा के लिए।
टैसोस बैसकोस

@BoratSagdiyev सबसे बड़ा कारण बंद का समर्थन करना और अधिक उपयोगी बनाना था। संग्रह देखें। List.sort () docs.oracle.com/javase/8/docs/api/java/util/… पर एक नजर डालें, उन्होंने बिना किसी विशिष्ट कार्यान्वयन को बदले, सभी सूचियों के लिए एक विधि जोड़ी है। उन्होंने Collection.removeIf () जोड़ा जो उपयोगी है
पीटर लॉरी

@TassosBassoukos +1 का कहना है कि आपके पास लिस्ट का अपना कार्यान्वयन है, अब आप myList.stream () इसे या myList.sort () अपने कोड को बदले बिना
पीटर लॉरी

3
@PeterLawrey: AB संकलन नहीं करेगा क्योंकि इसे ओवरराइड hi()(अस्पष्टता को ठीक करने के लिए) करना है। उदाहरण के लिए, इसे लागू A.super.hi()करने के लिए इसे उसी तरह लागू करने के लिए चुनें जैसे कि ए
जेबी निज़ेट

20

जहां तक ​​संकलक का संबंध है, वे दो विधियां समान हैं। दोनों का एक कार्यान्वयन होगा।

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


2
यह बताता है कि जावा आपको एक से अधिक कक्षाओं का विस्तार करने की अनुमति क्यों नहीं देता
आर्थर रोनाल्ड

1
@ArthurRonald, वास्तव में यह सिर्फ संबंधित दिखता है। हालाँकि, IMO, वर्ग जो एक से अधिक वर्ग का विस्तार करता है , डायमंड प्रॉब्लम में चला सकता है (जो कि सबसे अधिक व्युत्पन्न वर्ग में डुप्लीकेट ऑब्जेक्ट स्टेट है) और यही कारण है कि जावा अपने उपयोगकर्ताओं को परेशानियों से दूर ले गया। दूसरी ओर, वर्ग जो एक से अधिक वर्ग को लागू करता है वह कभी भी डायमंड समस्या में नहीं चल सकता है क्योंकि इंटरफ़ेस वस्तुओं को राज्य प्रदान नहीं करता है। और समस्या विशुद्ध रूप से सिंटैक्स सीमाओं के कारण है - फ़ंक्शन कॉल को पूरी तरह से अर्हता प्राप्त करने में असमर्थता।
uvsmtid

13

पहचान के लिए कुछ भी नहीं है। इंटरफेस केवल एक विधि का नाम और हस्ताक्षर के साथ मुकदमा चलाते हैं। यदि दोनों इंटरफेस में एक ही नाम और हस्ताक्षर की एक विधि है, तो कार्यान्वयन वर्ग एक ही ठोस विधि के साथ दोनों इंटरफ़ेस विधियों को लागू कर सकता है।

हालाँकि, यदि दो इंटरफ़ेस विधि के शब्दार्थ अनुबंध विपरीत हैं, तो आप बहुत अधिक खो चुके हैं; आप तब एक ही वर्ग में दोनों इंटरफेस को लागू नहीं कर सकते।


4

अनाम के रूप में इंटरफ़ेस को लागू करने का प्रयास करें।

public class MyClass extends MySuperClass implements MyInterface{

MyInterface myInterface = new MyInterface(){

/* Overrided method from interface */
@override
public void method1(){

}

};

/* Overrided method from superclass*/
@override
public void method1(){

}

}

4

जैसा कि इंटरफ़ेस में, हम केवल तरीकों की घोषणा कर रहे हैं, ठोस वर्ग जो इन दोनों इंटरफेसों को लागू करता है वह समझता है कि केवल एक ही विधि है (जैसा कि आपने वर्णित है कि रिटर्न प्रकार में दोनों का एक ही नाम है)। इसलिए इसके साथ कोई समस्या नहीं होनी चाहिए। आप उस विधि को ठोस वर्ग में परिभाषित कर पाएंगे।

लेकिन जब दो इंटरफ़ेस में एक ही नाम के साथ एक विधि होती है, लेकिन अलग-अलग रिटर्न टाइप होते हैं और आप दो तरीकों को कंक्रीट क्लास में लागू करते हैं:

कृपया नीचे दिए गए कोड को देखें:

public interface InterfaceA {
  public void print();
}


public interface InterfaceB {
  public int print();
}

public class ClassAB implements InterfaceA, InterfaceB {
  public void print()
  {
    System.out.println("Inside InterfaceA");
  }
  public int print()
  {
    System.out.println("Inside InterfaceB");
    return 5;
  }
}

जब कंपाइलर को "public void print ()" मेथड दिखाई देता है, तो यह पहली बार InterfaceA में दिखता है और यह इसे प्राप्त करता है। लेकिन अभी भी यह संकलन समय त्रुटि देता है कि वापसी प्रकार InterfaceB के तरीके के साथ संगत नहीं है।

तो यह संकलक के लिए haywire जाता है।

इस तरह, आप दो इंटरफ़ेस को लागू नहीं कर पाएंगे, जिसमें एक ही नाम की एक विधि होगी, लेकिन अलग-अलग रिटर्न प्रकार।


3

अगर वे दोनों एक ही हैं तो इससे कोई फर्क नहीं पड़ता। यह प्रति इंटरफ़ेस विधि के साथ एक ठोस विधि के साथ दोनों को लागू करता है।

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