एक इंटरफेस में कंस्ट्रक्टर?


148

मुझे पता है कि एक इंटरफ़ेस में एक कंस्ट्रक्टर को परिभाषित करना संभव नहीं है। लेकिन मैं सोच रहा हूं कि क्यों, क्योंकि मुझे लगता है कि यह बहुत उपयोगी हो सकता है।

तो आप यह सुनिश्चित कर सकते हैं कि इस इंटरफ़ेस के प्रत्येक कार्यान्वयन के लिए एक वर्ग के कुछ क्षेत्रों को परिभाषित किया गया है।

उदाहरण के लिए निम्न संदेश वर्ग पर विचार करें:

public class MyMessage {

   public MyMessage(String receiver) {
      this.receiver = receiver;
   }

   private String receiver;

   public void send() {
      //some implementation for sending the mssage to the receiver
   }
}

यदि इस वर्ग के लिए एक इंटरफ़ेस परिभाषित करें ताकि मेरे पास और अधिक कक्षाएं हों जो संदेश इंटरफ़ेस को लागू करें, मैं केवल भेजने की विधि को परिभाषित कर सकता हूं न कि निर्माता को। तो मैं यह कैसे सुनिश्चित कर सकता हूं कि इस वर्ग के प्रत्येक कार्यान्वयन में वास्तव में एक रिसीवर सेट है? अगर मैं इस पद्धति का उपयोग करता हूं तो मुझे setReceiver(String receiver)यकीन नहीं हो सकता है कि यह विधि वास्तव में कहलाती है। कंस्ट्रक्टर में मैं इसे सुनिश्चित कर सकता था।



2
आप कहते हैं, "कंस्ट्रक्टर में मैं यह सुनिश्चित कर सकता था कि [इस वर्ग के प्रत्येक कार्यान्वयन में वास्तव में एक रिसीवर सेट है]।" लेकिन नहीं, आप संभवतः ऐसा नहीं कर सकते। बशर्ते कि इस तरह के एक निर्माता को परिभाषित करना संभव था, पैरामीटर केवल आपके कार्यान्वयनकर्ताओं के लिए एक मजबूत संकेत होगा - लेकिन अगर वे चाहते थे तो बस इसे अनदेखा करने के लिए चुना जा सकता है।
जूलियन सिलैंड 3

3
@ मत्तब उम्म, यह एक अलग भाषा है।
यस

जवाबों:


129

आपके द्वारा बताई गई कुछ चीजों को लेना:

"तो आप यह सुनिश्चित कर सकते हैं कि इस इंटरफ़ेस के प्रत्येक कार्यान्वयन के लिए एक कक्षा में कुछ फ़ील्ड परिभाषित किए गए हैं।"

"अगर इस वर्ग के लिए एक इंटरफ़ेस परिभाषित करें ताकि मेरे पास अधिक कक्षाएं हो जो संदेश इंटरफ़ेस को लागू करें, मैं केवल भेजने की विधि को परिभाषित कर सकता हूं न कि रचनाकार को"

... ये आवश्यकताएं बिल्कुल वही हैं जो अमूर्त वर्ग के लिए हैं।


लेकिन ध्यान दें कि उपयोग मामला @Sebi वर्णन करता है (मूल रचनाकारों से अधिभार विधियों को कॉल करना) एक बुरा विचार है जैसा कि मेरे उत्तर में बताया गया है।
आरपी

44
मैट, यह स्पष्ट रूप से सच है, लेकिन अमूर्त वर्ग एकल-विरासत सीमा से ग्रस्त हैं, जो लोगों को पदानुक्रम निर्दिष्ट करने के अन्य तरीकों को देखने के लिए प्रेरित करता है।
CPerkins

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

7
@CPerkins यह सच है, मैं यह नहीं सुझाव दे रहा हूं कि बस एक अमूर्त वर्ग का उपयोग करने से सेबी का उपयोग मामला हल हो जाएगा। यदि कुछ भी है, तो एक Messageइंटरफ़ेस घोषित करना सबसे अच्छा है जो send()विधि को परिभाषित करता है , और अगर सेबी Messageइंटरफ़ेस के कार्यान्वयन के लिए "आधार" वर्ग प्रदान करना चाहता है , तो एक AbstractMessageसाथ ही प्रदान करें । अमूर्त वर्गों को इंटरफेस की जगह नहीं लेनी चाहिए, ऐसा सुझाव देने का प्रयास कभी नहीं किया गया था।
मैट बी

2
समझ गया, मैट। मैं आपके साथ बहस नहीं कर रहा था, इस ओर इशारा करते हुए कि यह ऑप्स के लिए एक पूर्ण प्रतिस्थापन नहीं है।
CPerkins

76

एक समस्या जो आपको तब मिलती है जब आप एक ही समय में कई इंटरफेस को लागू करने की संभावना से इंटरफेस में निर्माणकर्ताओं को अनुमति देते हैं। जब एक क्लास कई इंटरफेस को लागू करता है जो अलग-अलग कंस्ट्रक्टर को परिभाषित करता है, तो क्लास को कई कंस्ट्रक्टर्स को लागू करना होगा, प्रत्येक को केवल एक इंटरफ़ेस को संतुष्ट करना होगा, लेकिन अन्य को नहीं। इनमें से प्रत्येक निर्माता को कॉल करने वाली वस्तु का निर्माण करना असंभव होगा।

या कोड में:

interface Named { Named(String name); }
interface HasList { HasList(List list); }

class A implements Named, HasList {

  /** implements Named constructor.
   * This constructor should not be used from outside, 
   * because List parameter is missing
   */
  public A(String name)  { 
    ...
  }

  /** implements HasList constructor.
   * This constructor should not be used from outside, 
   * because String parameter is missing
   */
  public A(List list) {
    ...
  }

  /** This is the constructor that we would actually 
   * need to satisfy both interfaces at the same time
   */ 
  public A(String name, List list) {
    this(name);
    // the next line is illegal; you can only call one other super constructor
    this(list); 
  }
}

1
भाषा नहीं कर सकते हैंclass A implements Named, HashList { A(){HashList(new list()); Named("name");} }
mako

1
"एक इंटरफ़ेस में कंस्ट्रक्टर" के लिए सबसे उपयोगी अर्थ, अगर अनुमति है, तो new Set<Fnord>()इसका मतलब यह होगा कि "मुझे कुछ दे दो जिसे मैं Set<Fnord>" के रूप में उपयोग कर सकता हूं ; अगर लेखक का Set<T>उद्देश्य HashSet<T>उन चीजों के लिए कार्यान्वयन करना है, जिनकी किसी और चीज की विशेष आवश्यकता नहीं है, तो इंटरफ़ेस को फिर से परिभाषित किया new Set<Fnord>()जा सकता है new HashSet<Fnord>()। एक वर्ग के लिए कई इंटरफेस को लागू करने के लिए कोई समस्या नहीं होगी, क्योंकि new InterfaceName()बस इंटरफ़ेस द्वारा नामित एक वर्ग का निर्माण करेगा ।
सुपरकैट

जवाबी तर्क: अपने A(String,List)निर्माता नामित निर्माता हो सकता है, और A(String)और A(List)माध्यमिक जो कि इसे कहते हो सकता है। आपका कोड काउंटर-उदाहरण नहीं है, बस एक गरीब है।
बेन लेगिएरो

आप सभी निर्माणकर्ताओं को एक कार्यान्वयन में क्यों बुलाएंगे?! हाँ, यदि यह ctor के साथ अधिक Interfaces को लागू करता है, एक String के साथ और एक int के साथ, तो उसे दो cenders की आवश्यकता है - लेकिन या तो इसका उपयोग किया जा सकता है। यदि यह लागू नहीं होता है, तो क्लास दोनों इंटरफ़ेस को लागू नहीं करता है। तो क्या!? (हालांकि Interfaces में ctor नहीं होने के अन्य कारण हैं)।
काई

@kai नहीं, उदाहरण के लिए निर्माण के दौरान दोनों इंटरफ़ेस कंस्ट्रक्टर्स को कॉल करना होगा। दूसरे शब्दों में: मेरे उदाहरण में, उदाहरण में एक नाम और एक सूची दोनों हैं, इसलिए प्रत्येक उदाहरण को नाम और सूची दोनों को त्वरित करने की आवश्यकता है।
डैनियल कुल्मन 15

13

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

आपके द्वारा वर्णित उपयोग का मामला एक अमूर्त वर्ग के समान है, जिसमें निर्माणकर्ता एक अमूर्त विधि का एक तरीका कहता है जिसे एक बच्चे की कक्षा में लागू किया जाता है।

यहाँ अंतर्निहित समस्या यह है कि जब बेस कंस्ट्रक्टर को निष्पादित किया जा रहा है, तब तक चाइल्ड ऑब्जेक्ट का निर्माण नहीं किया गया है, और अप्रत्याशित अवस्था में है।

सारांशित करने के लिए: जब आप माइंडप्रोड को उद्धृत करने के लिए मूल निर्माणकर्ताओं से अतिभारित तरीके कहते हैं, तो यह परेशानी पूछ रहा है :

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


6

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

हालाँकि इस वर्कअराउंड के लिए आपको getInstance()इस इंटरफ़ेस की सभी वस्तुओं को तुरंत उपयोग करने की आवश्यकता होती है।

उदाहरण के लिए

public interface Module {
    Module getInstance(Receiver receiver);
}

5

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

दूसरा कारण-उपवर्ग के ऑब्जेक्ट निर्माण के दौरान, पेरेंट कंस्ट्रक्टर को कहा जाता है ।लेकिन यदि कोई एक से अधिक इंटरफ़ेस कार्यान्वित किया जाएगा, तो इंटरफ़ेस कंस्ट्रक्टर के कॉल के दौरान एक विरोधाभास हो जाएगा कि किस इंटरफ़ेस का निर्माता पहले कॉल करेगा


3

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

interface IMyMessage(){
    @NonNull String getReceiver();
}
  • यह एनकैप्सुलेशन नहीं तोड़ेगा
  • यह आपके इंटरफ़ेस का उपयोग करने वाले सभी को बताएगा कि Receiverऑब्जेक्ट को किसी तरह से कक्षा में पारित किया जाना है (या तो कंस्ट्रक्टर या सेटर्स द्वारा)

2

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


1

क्यों (टिप्पणियों से लिया गया) के लिए यह प्रश्न देखें ।

यदि आपको वास्तव में ऐसा कुछ करने की आवश्यकता है, तो आप इंटरफ़ेस के बजाय एक सार आधार वर्ग चाहते हैं।


1

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


0

यहाँ इस Technic का उपयोग करके एक उदाहरण दिया गया है। इस विशिष्ट उदाहरण में कोड फायरबस को एक मॉक का उपयोग करके एक कॉल कर रहा MyCompletionListenerहै जो कि एक सार वर्ग के रूप में मास्क किया गया इंटरफ़ेस है, एक कंस्ट्रक्टर के साथ एक इंटरफ़ेस

private interface Listener {
    void onComplete(databaseError, databaseReference);
}

public abstract class MyCompletionListener implements Listener{
    String id;
    String name;
    public MyCompletionListener(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

private void removeUserPresenceOnCurrentItem() {
    mFirebase.removeValue(child("some_key"), new MyCompletionListener(UUID.randomUUID().toString(), "removeUserPresenceOnCurrentItem") {
        @Override
        public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {

        }
    });
    }
}

@Override
public void removeValue(DatabaseReference ref, final MyCompletionListener var1) {
    CompletionListener cListener = new CompletionListener() {
                @Override
                public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                    if (var1 != null){
                        System.out.println("Im back and my id is: " var1.is + " and my name is: " var1.name);
                        var1.onComplete(databaseError, databaseReference);
                    }
                }
            };
    ref.removeValue(cListener);
}

आप किस प्रकार से privateपहुँच संशोधक का उपयोग कर सकते हैं interface?
रुद्र

0

आम तौर पर कंस्ट्रक्टर ऑब्जेक्ट के संबंध में विशेष वर्ग के गैर-स्थिर सदस्यों को प्रारंभिक करने के लिए होते हैं।

इंटरफ़ेस के लिए कोई ऑब्जेक्ट निर्माण नहीं है क्योंकि केवल घोषित तरीके हैं लेकिन परिभाषित तरीके नहीं हैं। हम घोषित तरीकों से वस्तु क्यों नहीं बना सकते, वस्तु-निर्माण कुछ भी नहीं है, लेकिन गैर-स्थैतिक सदस्यों के लिए कुछ मेमोरी (हीप मेमोरी में) आवंटित करना है।

JVM उन सदस्यों के लिए मेमोरी बनाएगा जो पूरी तरह से विकसित और उपयोग के लिए तैयार हैं। उन सदस्यों पर आधारित, JVM यह गणना करता है कि उनके लिए कितनी मेमोरी आवश्यक है और मेमोरी बनाता है।

घोषित तरीकों की जांच, जेवीएम इन घोषित तरीकों के लिए कितनी मेमोरी की आवश्यकता होगी इसकी गणना करने में असमर्थ है क्योंकि कार्यान्वयन भविष्य में होगा जो इस समय तक नहीं किया गया है। तो ऑब्जेक्ट निर्माण इंटरफ़ेस के लिए संभव नहीं है।

निष्कर्ष:

ऑब्जेक्ट निर्माण के बिना, एक कंस्ट्रक्टर के माध्यम से गैर-स्थिर सदस्यों को इनिशियलाइज़ करने का कोई मौका नहीं है। यही कारण है कि कंस्ट्रक्टर एक इंटरफ़ेस के अंदर नहीं है (क्योंकि एक इंटरफ़ेस के अंदर कंस्ट्रक्टर का कोई उपयोग नहीं है)

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