2017-05-09 59 views
6

मैं कुछ जावा 8 कोड है जो इस तरह दिखता है को बनाए रखने कर रहा हूँ के दौरान अधिभावी विधि:जावा - (बेनामी उपवर्ग) वस्तु दृष्टान्त निर्माण

Class Entity { 
    protected Model theModel; 

    public Entity() { 
     init(); 
    } 

    protected void init() { 
     this.theModel = new Model(); 
    } 
} 

Class Model { 
} 

Class SubModel extends Model { 
} 

main { 
    Entity newEntity = new Entity() { 
     @Override 
     protected void init() { 
      this.theModel = new SubModel(); 
     } 
    }; 
} 

कोड वर्तमान में संकलित और सही ढंग से चलाता है, लेकिन अब मैं इसे अद्यतन करने की जरूरत है।

मेरा प्रश्न हैं:

  1. कैसे init() विधि का ओवरराइड, सब पर काम कर रहा है newEntity के निर्माण के दौरान?
  2. ऑब्जेक्ट कन्स्ट्रक्टर स्टेटमेंट में इस विधि ओवरराइड के लिए सही शब्दावली क्या है?

मेरा शोध अब तक सुझाव देता है कि जावा गतिशील रूप से विधियों को ओवरराइड नहीं कर सकता है - इस आधार पर ओवरराइड नहीं कर सकता है, क्योंकि विधि ओवरराइड प्रति-वर्ग प्रति-वस्तु नहीं है। लेकिन यह कोड स्निपेट दिखाना प्रतीत होता है कि जावा इसे अभ्यास में कर सकता है?

+2

कृपया [इस सवाल] को पढ़ें (http://stackoverflow.com/q/28676796/1876620) और उत्तर। यह 'इस संदर्भ' से बचने के बारे में है। –

उत्तर

6

जहां तक ​​मैं कह सकता हूं कि यहां कुछ खास नहीं है, केवल शास्त्रीय constructor chaining है और बहुरूपता वर्चुअल विधि आमंत्रण पर लागू होती है।

जब आप अपने गुमनाम वर्ग का दृष्टांत, यह स्वतः ही लागू करेगा अपने default constructor (जो स्वचालित रूप से संकलक द्वारा दिया जाता है), इससे पहले कि उसके डिफ़ॉल्ट निर्माता सफल होता है यह पहली बार अपनी मूल वर्ग डिफ़ॉल्ट निर्माता, जो बारी में init() विधि लागू करेगा आह्वान चाहिए , जो, चूंकि इसे आपके अज्ञात वर्ग द्वारा ओवरराइड किया गया है, बहुरूप रूप से, बच्चे वर्ग में init विधि को कॉल करना समाप्त करता है, जो मॉडल को आपके SubModel उदाहरण में प्रारंभ करता है। ": विरासत के लिए डिजाइन और दस्तावेज़ वरना निषेध मद 17" उन्होंने लिखा है:

"हैं

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

वह तो एक उदाहरण है जो आप अच्छी तरह से करना होगा अध्ययन करने के लिए देने के लिए आगे बढ़ता है:

" यहाँ एक उपवर्ग कि overrideMe ओवरराइड करता है इस ठोस करने के लिए यहां एक वर्ग है कि इस नियम का उल्लंघन करती है , जो विधि है ग़लती से Super की एकमात्र निर्माता द्वारा अनुरोध किया गया: "

public class Super { 
    // Broken - constructor invokes an overridable method 
    public Super() { 
        overrideMe(); 
    } 

    public void overrideMe() { 
    } 
} 

public final class Sub extends Super { 
    private final Date date; // Blank final, set by constructor 

    Sub() { 
        date = new Date(); 
    } 

    // Overriding method invoked by superclass constructor 
    @Override public void overrideMe() { 
        System.out.println(date); 
    } 

    public static void main(String[] args) { 
        Sub sub = new Sub(); 
        sub.overrideMe(); 
    } 
} 

" आप दो बार तारीख प्रिंट करने के लिए इस कार्यक्रम की उम्मीद कर सकते, ख यह पहली बार शून्य से प्रिंट करता है, क्योंकि overrideMe विधि Sub से पहले सुपर कन्स्ट्रक्टर द्वारा आविष्कार किया गया हैदिनांक फ़ील्ड प्रारंभ करने का मौका है। ध्यान दें कि यह प्रोग्राम दो अलग-अलग राज्यों में अंतिम फ़ील्ड देखता है! यह भी ध्यान दें अगर overrideMe था किसी भी तरीके से लागू किया है कि date पर, मंगलाचरण फेंक दिया है | एक NullPointerExceptionSuper निर्माता लागू जब overrideMe। एकमात्र कारण इस कार्यक्रम तो

फेंक नहीं है एक NullPointerException के रूप में यह खड़ा है कि println विधि एक शून्य तर्क के साथ काम कर रहा लिए विशेष प्रावधान नहीं है। ", जैसा कि आप देख सकते हैं, और यहोशू बलोच के रूप में इतनी अच्छी तरह से समझाया गया है कि जोखिम छाया में छिपे हुए हैं: आप ओवरराइड विधि में क्या कर सकते हैं, इसकी संभावनाओं में, जहां आपके पास आवृत्ति चर को छूने का लाइसेंस है कि कन्स्ट्रक्टर चेन को अभी तक प्रारंभ करने का मौका नहीं मिला है। मुद्दा यह है कि जब तक आप कन्स्ट्रक्टर श्रृंखला द्वारा पूरी तरह से प्रारंभ नहीं किया जाता है तब तक आपको ऑब्जेक्ट स्थिति को स्पर्श करने की अनुमति नहीं दी जानी चाहिए।

आप कह सकते हैं कि आपके विशेष मामले में ऐसा नहीं होता है, क्योंकि आप अवैध रूप से राज्य में बदलाव नहीं कर रहे हैं और आपकी ओवरराइड विधि सुरक्षित है, सार्वजनिक नहीं है, लेकिन समस्या यह है कि इस कोड को छूने वाले किसी भी व्यक्ति को बहुत स्पष्ट समझ की आवश्यकता है ये सभी चीजें हुड के नीचे हो रही हैं, जो आपके वर्तमान कोड के अलावा अन्य जगहों पर हो रही हैं। रखरखाव के दौरान गंभीर गलती करना आसान होता है, खासकर जब आप या कुछ अन्य डेवलपर, मूल रूप से परिभाषित होने के बाद संभवतः महीनों या यहां तक ​​कि वर्षों के बदलाव करने के लिए यहां आते हैं, और इन सभी खतरों के संदर्भ को खोने से कोई व्यक्ति एक बग पेश करता है खोजने और ठीक करने में वाकई मुश्किल होगी।

+5

इस उत्कृष्ट उत्तर के लिए धन्यवाद। StackOverflow के बारे में अच्छा सब कुछ उदाहरण देता है। – radfast

5

यदि वास्तव में आप हमें दिखा रहे हैं, और तस्वीर गायब होने का कोई महत्वपूर्ण हिस्सा नहीं है, तो कोड जो आपको बनाए रखना है वह खराब है, और खराब कोड का रखरखाव बहुत परेशानी है।

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

और समय बीतने के साथ चीजें अधिक जटिल हो जाती हैं।

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

इस विधि ओवरराइड वस्तु निर्माता में शामिल करने के लिए सही शब्दावली है: गलत

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

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

यह सी ++ से अलग है, जहां निर्माण समय पर प्रभावशाली वर्चुअल विधि तालिका कन्स्ट्रक्टर घोषित करने वाली कक्षा की वर्चुअल विधि तालिका है, (भले ही इसे उप-वर्गीकृत किया गया हो), ताकि जब आप वर्चुअल विधि को भीतर से कॉल करें एक सी ++ कन्स्ट्रक्टर आप किसी भी ओवरराइडिंग विधियों का आह्वान नहीं कर रहे हैं।

+2

इस बेहद उपयोगी उत्तर के लिए धन्यवाद। अब मैं समझता हूं कि मेरी शब्दावली गलत थी, यह एक गतिशील ओवरराइड नहीं है: यह एक अज्ञात सबक्लास है। मैं देखूंगा कि मैं इस कार्यक्षमता को कैसे सुधार सकता हूं, वर्तमान कार्यक्षमता को नष्ट किए बिना ... – radfast

+2

और "गलत" पर लॉल! – radfast

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