जावा में विरासत को प्रतिबंधित करने के अच्छे कारण?


178

जावा में वंशानुक्रम को प्रतिबंधित करने के लिए अच्छे कारण क्या हैं, उदाहरण के लिए एक एकल, निजी पैरामीटर रहित कंस्ट्रक्टर का उपयोग करके अंतिम वर्गों या कक्षाओं का उपयोग करना? विधि को अंतिम बनाने के अच्छे कारण क्या हैं?


1
पैंडेंट्री: यदि आप अपने कोड में स्पष्ट रूप से एक नहीं लिखते हैं तो डिफ़ॉल्ट कंस्ट्रक्टर जावा कंपाइलर द्वारा आपके लिए जनरेट किया गया है। आपका मतलब नो लॉजिक कंस्ट्रक्टर से है।
जॉन टॉपले

कि "निहित डिफ़ॉल्ट निर्माता नहीं है"?
cretzel

2
"आपको अपनी कक्षा के लिए कोई भी कंस्ट्रक्टर प्रदान करने की आवश्यकता नहीं है, लेकिन ऐसा करते समय आपको सावधान रहना होगा। कंपाइलर अपने आप ही बिना कंस्ट्रक्टर के किसी भी वर्ग के लिए नो-तर्क, डिफ़ॉल्ट कंस्ट्रक्टर प्रदान करता है।" java.sun.com/docs/books/tutorial/java/javaOO/constructors.html - John Topley
John Topley

जवाबों:


184

यहां आपका सबसे अच्छा संदर्भ यहोशू बलोच की उत्कृष्ट पुस्तक "प्रभावी जावा" का आइटम 19 है, जिसे "डिजाइन और दस्तावेज़ विरासत के लिए कहा जाता है या इसे निषिद्ध करें"। (यह दूसरे संस्करण में आइटम 17 है और पहले संस्करण में आइटम 15 है।) आपको वास्तव में इसे पढ़ना चाहिए, लेकिन मैं संक्षेप में बताऊंगा।

अपने माता-पिता के साथ विरासत में मिली कक्षाओं की बातचीत अगर पूर्वज से विरासत में लेने के लिए डिज़ाइन नहीं की गई तो आश्चर्य और अप्रत्याशित हो सकती है।

इसलिए कक्षाएं दो प्रकारों में आनी चाहिए:

  1. विस्तारित किए जाने के लिए डिज़ाइन की गई कक्षाएं , और यह कैसे किया जाना चाहिए, इसका वर्णन करने के लिए पर्याप्त प्रलेखन के साथ

  2. कक्षाएं अंतिम रूप से चिह्नित हैं

यदि आप विशुद्ध रूप से आंतरिक कोड लिख रहे हैं तो यह थोड़ा अधिक हो सकता है। हालाँकि एक कक्षा फ़ाइल में पाँच वर्ण जोड़ने का अतिरिक्त प्रयास बहुत कम है। यदि आप केवल आंतरिक comsumption के लिए लिख रहे हैं तो एक भविष्य कोडर हमेशा 'अंतिम' निकाल सकता है - आप इसे एक चेतावनी के रूप में सोच सकते हैं "यह वर्ग मन में विरासत के साथ नहीं बनाया गया था"।


6
मेरे अनुभव में यह ओवरकिल नहीं है अगर मेरे अलावा कोई भी कोड का उपयोग कर रहा होगा। मुझे समझ में नहीं आया है कि जावा में डिफ़ॉल्ट रूप से अधिक होने के तरीके क्यों हैं।
बजे एरिक वेलना

9
कुछ प्रभावी जावा को समझने के लिए, आपको जोश के डिजाइन को समझने की आवश्यकता है। वह कहते हैं [कुछ ऐसा] आपको हमेशा वर्ग इंटरफेस डिज़ाइन करना चाहिए जैसे कि वे एक सार्वजनिक एपीआई थे। मुझे लगता है कि बहुमत की राय यह है कि यह दृष्टिकोण आमतौर पर बहुत भारी और अनम्य है। (YAGNI, आदि)
टॉम हैटिन -

9
अच्छा उत्तर। (मैं यह इंगित करने का विरोध नहीं कर सकता कि यह छह वर्ण है, क्योंकि वहाँ भी एक स्थान है ... ;-)
joel.neely

36
कृपया ध्यान दें, बनाने की कक्षाएं अंतिम हो सकता है मेकअप कठिन परीक्षण है कि (कठिन ठूंठ या नकली करने के लिए)
notnoop

10
यदि अंतिम वर्ग एक इंटरफ़ेस पर लिख रहा है, तो मॉकिंग एक समस्या नहीं होनी चाहिए।
फ्रेड हसलाम

26

आप एक विधि को अंतिम रूप देना चाहते हैं ताकि ओवरराइडिंग क्लासेस व्यवहार को बदल न सकें जो अन्य विधियों में गिना जाता है। कंस्ट्रक्टरों में कहे जाने वाले तरीके अक्सर फाइनल घोषित कर दिए जाते हैं ताकि आपको ऑब्जेक्ट बनाते समय कोई अप्रिय आश्चर्य न हो।


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

1
@Nav यदि आप एक विधि को अंतिम बनाते हैं, तो एक ओवरराइडिंग क्लास उस विधि का अपना संस्करण नहीं बना पाएगी (या यह एक संकलन त्रुटि होगी)। इस तरह से आप जानते हैं कि आपके द्वारा उस तरीके को लागू करने का व्यवहार बाद में तब नहीं बदलेगा जब कोई अन्य व्यक्ति क्लास को बढ़ाएगा।
छिपकली

19

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


15

3 उपयोग मामले हैं जहां आप अंतिम तरीकों के लिए जा सकते हैं।

  1. एक विशेष आधार वर्ग की कार्यक्षमता को ओवरराइड करने से व्युत्पन्न वर्ग से बचने के लिए।
  2. यह सुरक्षा उद्देश्य के लिए है, जहां आधार वर्ग ढांचे की कुछ महत्वपूर्ण कोर कार्यक्षमता दे रहा है, जहां व्युत्पन्न वर्ग इसे बदलने के लिए नहीं माना जाता है।
  3. अंतिम विधियाँ उदाहरण विधियों की तुलना में तेज़ होती हैं, क्योंकि अंतिम और निजी तरीकों के लिए वर्चुअल टेबल अवधारणा का कोई उपयोग नहीं होता है। इसलिए जहां कभी भी संभावना है, अंतिम तरीकों का उपयोग करने का प्रयास करें।

कक्षा को अंतिम बनाने का उद्देश्य:

ताकि कोई भी निकाय उन वर्गों का विस्तार न कर सके और अपना व्यवहार बदल सके।

जैसे: रैपर क्लास इंटेगर एक अंतिम वर्ग है। यदि वह वर्ग अंतिम नहीं है, तो कोई भी इंटेगर को अपनी कक्षा में विस्तारित कर सकता है और पूर्णांक वर्ग के मूल व्यवहार को बदल सकता है। इससे बचने के लिए, जावा ने सभी रैपर वर्गों को अंतिम कक्षाओं के रूप में बनाया।


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

4
यहां तक ​​कि अगर आप सभी चर और तरीकों को अंतिम बनाते हैं, तब भी अन्य लोग आपकी कक्षा को विरासत में दे सकते हैं और अतिरिक्त कार्यक्षमता जोड़ सकते हैं और अपनी कक्षा के मूल व्यवहार को बदल सकते हैं।
user1923551

3
> यदि वह वर्ग अंतिम नहीं है, तो कोई भी इंटेगर को अपनी कक्षा में विस्तारित कर सकता है और पूर्णांक वर्ग के मूल व्यवहार को बदल सकता है। मान लें कि यह अनुमति दी गई थी और इस नए वर्ग को बुलाया गया था DerivedInteger, यह अभी भी मूल इंटेगर वर्ग को नहीं बदलता है, और जो कोई भी DerivedIntegerअपने जोखिम पर ऐसा करता है, इसलिए मुझे अभी भी नहीं मिलता है कि यह एक समस्या क्यों है।
सिद्धार्थ

12

आप अपरिवर्तनीय वस्तुएं बनाना चाहते हैं ( http://en.wikipedia.org/wiki/Immutable_object ), आप एक सिंगलटन ( http://en.wikipedia.org/wiki/Singleton_pattern ) बनाना चाहते हैं , या आप चाहते हो सकते हैं किसी को दक्षता, सुरक्षा या सुरक्षा के कारणों के लिए विधि को ओवरराइड करने से रोकने के लिए।


7

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

प्रभावी जावा 2 संस्करण आइटम 16 और 17, या मेरा ब्लॉग पोस्ट "इनहेरिटेंस टैक्स" देखें


ब्लॉग पोस्ट का लिंक टूट गया है। इसके अलावा, क्या आपको लगता है कि आप इस पर थोड़ा और विस्तार कर सकते हैं?

@MichaelT: फिक्स्ड लिंक, धन्यवाद - अधिक विस्तार के लिए इसका अनुसरण करें :) (मेरे पास इस समय इसे जोड़ने का समय नहीं है, मुझे डर है।)
जॉन स्कीट

@JonSkeet, ब्लॉग पोस्ट का लिंक टूट गया है।
लूसिफ़ेर

@ केदारनाथ: यह मेरे लिए काम करता है ... यह थोड़ी देर के लिए टूट गया था , लेकिन अब यह कोडबोल.जॉन्स्केट.यूके पर सही पृष्ठ की ओर इशारा कर रहा है ...
जॉन स्कीट

4

हम्म् ... मैं दो बातें सोच सकता हूं:

आपके पास एक वर्ग हो सकता है जो कुछ सुरक्षा मुद्दों से निपटता है। इसे उप-वर्ग करके और आपके सिस्टम को इसके उप-संस्करण को खिलाकर, एक हमलावर सुरक्षा प्रतिबंधों को दरकिनार कर सकता है। उदाहरण के लिए, आपका एप्लिकेशन प्लग इन का समर्थन कर सकता है और यदि कोई प्लग इन आपकी सुरक्षा संबंधित कक्षाओं को उपवर्गित कर सकता है, तो वह इस ट्रिक का उपयोग किसी भी तरह से उप-वर्ग किए गए संस्करण को स्मगल करने के लिए कर सकता है। हालाँकि, यह कुछ ऐसा है जो सूर्य को एप्लेट और इस तरह से संबंधित है, शायद ऐसा कोई यथार्थवादी मामला नहीं है।

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

 String blah = someOtherString;

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


2

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


2

यदि आप वर्गों और विधियों को अंतिम रूप से चिह्नित करते हैं, तो आप एक छोटे प्रदर्शन लाभ को देख सकते हैं, क्योंकि रनटाइम को किसी दिए गए ऑब्जेक्ट के लिए आमंत्रित करने के लिए सही वर्ग विधि नहीं दिखती है। गैर-अंतिम तरीकों को आभासी के रूप में चिह्नित किया जाता है ताकि जरूरत पड़ने पर उन्हें ठीक से बढ़ाया जा सके, अंतिम तरीकों को कक्षा में सीधे लिंक या संकलित इनलाइन किया जा सके।


1
मेरा मानना ​​है कि यह एक मिथक है, क्योंकि वर्तमान पीढ़ी वीएम को आवश्यकतानुसार अनुकूलन और अपनाने में सक्षम होना चाहिए। मुझे याद है कि कुछ लोगों ने इसे देखकर इसे मिथक के रूप में वर्गीकृत किया।
मिगेल पिंग

1
कोड पीढ़ी में अंतर एक मिथक नहीं है, लेकिन शायद प्रदर्शन लाभ हैं। जैसा कि मैंने कहा कि इसका एक छोटा लाभ है, एक साइट ने 3.5% प्रदर्शन लाभ का उल्लेख किया है, कुछ अधिक है, जो ज्यादातर मामलों में आपके कोड पर सभी नाजी जाने के लायक नहीं है।
1

1
यह कोई मिथक नहीं है। वास्तव में, कंपाइलर केवल वी अंतिम तरीकों को इनलाइन कर सकता है। मुझे यकीन नहीं है कि कोई भी जेआईटी "इनलाइन" चीजें कला रनटाइम करता है, लेकिन मुझे संदेह है कि यह तब हो सकता है जब आप बाद में एक व्युत्पन्न वर्ग लोड कर रहे हों।
उरई

1
Microbenchmark == अनाज के- नमक। 10B चक्र वार्मअप के बाद, 10B कॉल पर एवीजी अनिवार्य रूप से मेरे लैपटॉप पर ग्रहण के तहत कोई अंतर नहीं दिखाता है। स्ट्रिंग वापस करने वाले दो तरीके, फाइनल 6.9643us, गैर-फाइनल 6.9641us था। वह डेल्टा पृष्ठभूमि शोर, IMHO है।
joel.neely

1

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


2
वहाँ कुछ है जो मुझे उस तर्क के बारे में समझ में नहीं आता है - अगर किसी ने उन गणनाओं / स्थिरांक को बदल दिया है जो नहीं बदलना चाहिए - तो उस 100% गलती के कारण कोई भी विफलता नहीं है? दूसरे शब्दों में, परिवर्तन से कुछ भी कैसे लाभ होता है?
मैट बी

हां, यह तकनीकी रूप से "उनकी" गलती है, लेकिन किसी और का उपयोग करके पुस्तकालय के बारे में कल्पना करें जिसे परिवर्तनों से अवगत नहीं कराया गया है, जिससे भ्रम पैदा हो सकता है। मैंने हमेशा सोचा है कि यही कारण था कि जावा बेस कक्षाओं में से कई को अंतिम रूप से चिह्नित किया गया था, ताकि लोगों को बुनियादी कार्यक्षमता में परिवर्तन करने से रोका जा सके।
अनीस स्मिथ

@matt b - आपको लगता है कि डेवलपर ने जो बदलाव किया है उसने ऐसा जानबूझकर किया है या वे परवाह करेंगे कि यह उनकी गलती थी। अगर मेरे अलावा कोई मेरे कोड का उपयोग कर रहा है, तो मैं इसे अंतिम रूप से चिह्नित करूंगा जब तक कि इसे बदलने का मतलब नहीं है।
बजे एरिक वेलनाउ

यह वास्तव में 'अंतिम' का एक अलग उपयोग है जिसके बारे में पूछा गया है।
डीजेकेवर्थ

यह जवाब एक उपवर्ग लिखने की क्षमता के साथ व्यक्तिगत क्षेत्रों की विरासत और परिवर्तनशीलता को भ्रमित करने के लिए लगता है। आप वंशानुक्रम को प्रतिबंधित किए बिना व्यक्तिगत क्षेत्रों और विधियों को अंतिम (उनके पुनर्वितरण को रोकने) के रूप में चिह्नित कर सकते हैं।
joel.neely

0

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

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