java - रचनाकारों में अतिरंजित विधि कॉल के साथ क्या गलत है?




oop inheritance (5)

मेरे पास एक विकेट पेज क्लास है जो एक अमूर्त विधि के परिणाम के आधार पर पृष्ठ शीर्षक सेट करता है।

public abstract class BasicPage extends WebPage {

    public BasicPage() {
        add(new Label("title", getTitle()));
    }

    protected abstract String getTitle();

}

NetBeans मुझे "कन्स्ट्रक्टर में अतिरंजित विधि कॉल" संदेश के साथ चेतावनी देता है, लेकिन इसके साथ क्या गलत होना चाहिए? एकमात्र विकल्प मैं कल्पना कर सकता हूं कि सबक्लास में सुपर कन्स्ट्रक्टर को अन्यथा अमूर्त तरीकों के परिणाम पास करना है। लेकिन कई मानकों के साथ पढ़ना मुश्किल हो सकता है।


रचनाकारों से अतिरंजित विधि का आह्वान करने पर

सीधे शब्दों में कहें, यह गलत है क्योंकि यह अनावश्यक रूप से कई बगों को संभावनाएं खुलता है। जब @Override का आह्वान किया जाता है, तो वस्तु की स्थिति असंगत और / या अपूर्ण हो सकती है।

प्रभावी जावा द्वितीय संस्करण से एक उद्धरण , आइटम 17: विरासत के लिए डिज़ाइन और दस्तावेज़, या अन्यथा इसे प्रतिबंधित करें :

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

उदाहरण के लिए यहां एक उदाहरण दिया गया है:

public class ConstructorCallsOverride {
    public static void main(String[] args) {
        abstract class Base {
            Base() { overrideMe(); }
            abstract void overrideMe(); 
        }
        class Child extends Base {
            final int x;
            Child(int x) { this.x = x; }
            @Override void overrideMe() {
                System.out.println(x);
            }
        }
        new Child(42); // prints "0"
    }
}

यहां, जब Base कन्स्ट्रक्टर overrideMe , तो Child ने final int x प्रारंभ करना समाप्त नहीं किया है, और विधि गलत मान पाती है। यह लगभग निश्चित रूप से बग और त्रुटियों का कारण बन जाएगा।

संबंधित सवाल

यह भी देखें

कई मानकों के साथ वस्तु निर्माण पर

कई मापदंडों के साथ रचनाकार खराब पठनीयता का कारण बन सकते हैं, और बेहतर विकल्प मौजूद हैं।

यहां प्रभावी जावा द्वितीय संस्करण से एक उद्धरण दिया गया है , आइटम 2: कई कन्स्ट्रक्टर पैरामीटर का सामना करते समय एक निर्माता पैटर्न पर विचार करें :

परंपरागत रूप से, प्रोग्रामर ने टेलीस्कोपिंग कन्स्ट्रक्टर पैटर्न का उपयोग किया है, जिसमें आप केवल आवश्यक पैरामीटर के साथ एक कन्स्ट्रक्टर प्रदान करते हैं, दूसरा एक वैकल्पिक पैरामीटर के साथ, दो वैकल्पिक पैरामीटर के साथ एक तिहाई, और इसी तरह ...

दूरबीन कन्स्ट्रक्टर पैटर्न अनिवार्य रूप से इस तरह कुछ है:

public class Telescope {
    final String name;
    final int levels;
    final boolean isAdjustable;

    public Telescope(String name) {
        this(name, 5);
    }
    public Telescope(String name, int levels) {
        this(name, levels, false);
    }
    public Telescope(String name, int levels, boolean isAdjustable) {       
        this.name = name;
        this.levels = levels;
        this.isAdjustable = isAdjustable;
    }
}

और अब आप निम्न में से कोई भी कर सकते हैं:

new Telescope("X/1999");
new Telescope("X/1999", 13);
new Telescope("X/1999", 13, true);

हालांकि, आप वर्तमान में केवल name सेट नहीं कर सकते हैं और isAdjustable , और डिफ़ॉल्ट रूप से levels छोड़ रहे levels । आप अधिक कन्स्ट्रक्टर अधिभार प्रदान कर सकते हैं, लेकिन जाहिर है कि संख्याएं विस्फोट की संख्या के रूप में विस्फोट हो जाएंगी, और आपके पास कई boolean और int तर्क भी हो सकते हैं, जो वास्तव में चीजों से गड़बड़ कर देगा।

जैसा कि आप देख सकते हैं, यह लिखने के लिए एक सुखद पैटर्न नहीं है, और यहां तक ​​कि उपयोग करने के लिए भी कम सुखद है (यहां "सत्य" का अर्थ क्या है? 13 क्या है?)।

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

Telescope telly = new Telescope.Builder("X/1999").setAdjustable(true).build();

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

यह भी देखें

संबंधित सवाल


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

आपके उदाहरण में, क्या होता है यदि उप-वर्ग getTitle() और शून्य लौटाता है?

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


यदि आप अपने कन्स्ट्रक्टर में विधियों को कॉल करते हैं जो सबक्लास ओवरराइड करते हैं, तो इसका मतलब है कि आप उन चरों को संदर्भित करने की संभावना कम हैं जो अभी तक मौजूद नहीं हैं यदि आप अपने प्रारंभिकरण को रचनाकार और विधि के बीच तार्किक रूप से विभाजित करते हैं।

इस नमूना लिंक पर एक नज़र डालें javapractices.com/topic/TopicAction.do?Id=215


यहां एक उदाहरण दिया गया है जो इसे समझने में मदद करता है:

public class Main {
    static abstract class A {
        abstract void foo();
        A() {
            System.out.println("Constructing A");
            foo();
        }
    }

    static class C extends A {
        C() { 
            System.out.println("Constructing C");
        }
        void foo() { 
            System.out.println("Using C"); 
        }
    }

    public static void main(String[] args) {
        C c = new C(); 
    }
}

यदि आप यह कोड चलाते हैं, तो आपको निम्न आउटपुट मिलता है:

Constructing A
Using C
Constructing C

आप समझ सकते हैं? foo() सी के कन्स्ट्रक्टर चलाने से पहले सी का उपयोग करता है। अगर foo() को सी को एक परिभाषित राज्य (यानी कन्स्ट्रक्टर समाप्त हो गया है foo() आवश्यकता होती है, तो उसे सी में एक अपरिभाषित स्थिति का सामना करना पड़ेगा और चीजें तोड़ सकती हैं। और चूंकि आप ए में नहीं जानते कि ओवरराइट किए गए foo() अपेक्षा क्या है, आपको चेतावनी मिलती है।


विकेट के विशिष्ट मामले में: यही कारण है कि मैंने विकेट देवताओं को एक घटक बनाने के ढांचे के जीवन चक्र में एक स्पष्ट दो चरण घटक प्रारंभिक प्रक्रिया के लिए समर्थन जोड़ने के लिए कहा था यानी

  1. निर्माण - कन्स्ट्रक्टर के माध्यम से
  2. प्रारंभ - onInitilize के माध्यम से (निर्माण के बाद जब आभासी तरीकों काम करते हैं!)

इस बारे में काफी सक्रिय बहस थी कि यह आवश्यक था या नहीं (यह पूरी तरह से आवश्यक आईएमएचओ है) क्योंकि यह लिंक http://apache-wicket.1842946.n4.nabble.com/VOTE-WICKET-3218-Component-onInitialize-is-broken-for-Pages-td3341090i20.html )

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







override