जावा कई उत्तराधिकार की अनुमति नहीं देता है, लेकिन यह कई इंटरफेस को लागू करने की अनुमति देता है। क्यों?
जावा कई उत्तराधिकार की अनुमति नहीं देता है, लेकिन यह कई इंटरफेस को लागू करने की अनुमति देता है। क्यों?
जवाबों:
क्योंकि इंटरफेस केवल यह बताता है कि वर्ग क्या कर रहा है, न कि यह कैसे कर रहा है।
मल्टीपल इनहेरिटेंस के साथ समस्या यह है कि दो वर्ग एक ही काम करने के विभिन्न तरीकों को परिभाषित कर सकते हैं , और उपवर्ग यह नहीं चुन सकता है कि किसको चुनना है।
मेरे कॉलेज के प्रशिक्षकों में से एक ने मुझे इस तरह समझाया:
मान लीजिए कि मेरे पास एक वर्ग है, जो एक टोस्टर है, और दूसरा वर्ग है, जो न्यूक्लियरबॉम्ब है। वे दोनों एक "अंधेरा" सेटिंग हो सकती है। वे दोनों एक () विधि है। (एक बंद है (), दूसरा नहीं।) अगर मैं एक वर्ग बनाना चाहता हूं जो इन दोनों का उपवर्ग हो ... जैसा कि आप देख सकते हैं, यह एक समस्या है जो वास्तव में मेरे चेहरे पर उड़ सकती है। ।
तो मुख्य मुद्दों में से एक यह है कि यदि आपके पास दो मूल वर्ग हैं, तो उनके पास एक ही विशेषता के अलग-अलग कार्यान्वयन हो सकते हैं - या संभवतः एक ही नाम के साथ दो अलग-अलग विशेषताएं, जैसा कि मेरे प्रशिक्षक के उदाहरण में है। फिर आपको यह तय करने से निपटना होगा कि आपका कौन सा उपवर्ग उपयोग करने जा रहा है। इसे संभालने के तरीके हैं, निश्चित रूप से - सी ++ ऐसा करता है - लेकिन जावा के डिजाइनरों ने महसूस किया कि इससे चीजें बहुत जटिल हो जाएंगी।
एक इंटरफेस के साथ, हालांकि, आप कुछ ऐसा कर रहे हैं जो कक्षा को करने में सक्षम है, बजाय कुछ करने के दूसरे वर्ग के तरीके को उधार लेने में। कई इंटरफेस बहुत कम संभावना है कि मुश्किल संघर्षों का कारण बनता है जो कई मूल वर्गों की तुलना में हल करने की आवश्यकता होती है।
क्योंकि वंशानुक्रम का उपयोग तब भी किया जाता है जब आप यह नहीं कह सकते कि "अरे, यह तरीका उपयोगी लगता है, मैं उस वर्ग को भी बढ़ा दूंगा"।
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
इस प्रश्न का उत्तर जावा कंपाइलर (कंस्ट्रक्टर चेनिंग) के आंतरिक कामकाज में निहित है। यदि हम जावा कंपाइलर के आंतरिक कार्य को देखते हैं:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
public void printBankBalance(){
System.out.println("20k");
}
}
इस रूप को संकलित करने के बाद:
public class Bank {
public Bank(){
super();
}
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank {
SBI(){
super();
}
public void printBankBalance(){
System.out.println("20k");
}
}
जब हम कक्षा का विस्तार करते हैं और इसकी एक वस्तु बनाते हैं, तो एक निर्माण श्रृंखला Object
कक्षा तक चलेगी ।
ऊपर कोड ठीक चलेगा। लेकिन अगर हमारे पास एक और वर्ग है Car
जो विस्तारित होता है Bank
और एक हाइब्रिड (एकाधिक विरासत) वर्ग कहा जाता है SBICar
:
class Car extends Bank {
Car() {
super();
}
public void run(){
System.out.println("99Km/h");
}
}
class SBICar extends Bank, Car {
SBICar() {
super(); //NOTE: compile time ambiguity.
}
public void run() {
System.out.println("99Km/h");
}
public void printBankBalance(){
System.out.println("20k");
}
}
इस मामले में (SBICAR) कंस्ट्रक्टर चेन ( संकलन समय अस्पष्टता ) बनाने में विफल रहेगा ।
इंटरफेस के लिए इसे अनुमति दी जाती है क्योंकि हम इसका एक ऑब्जेक्ट नहीं बना सकते हैं।
नई अवधारणा default
और static
विधि के लिए कृपया इंटरफ़ेस में डिफ़ॉल्ट देखें ।
आशा है कि यह आपकी क्वेरी को हल करेगा। धन्यवाद।
कई इंटरफेस को लागू करना बहुत उपयोगी है और इससे न तो भाषा कार्यान्वयनकर्ताओं और न ही प्रोग्रामरों को बहुत समस्या होती है। तो इसकी अनुमति है। मल्टीपल इनहेरिटेंस, जबकि उपयोगी भी, उपयोगकर्ताओं के लिए गंभीर समस्याएं पैदा कर सकता है ( मौत का भयानक हीरा )। और कई चीजें जो आप कई विरासतों के साथ करते हैं, उन्हें रचना या आंतरिक कक्षाओं का उपयोग करके भी किया जा सकता है। इसलिए लाभ की तुलना में अधिक समस्याओं को लाने के रूप में कई वंशानुक्रम निषिद्ध है।
ToyotaCar
और HybridCar
दोनों Car
ओवररोड से प्राप्त हुए हैं Car.Drive
, और यदि PriusCar
दोनों को विरासत में मिला है , लेकिन ओवरराइड नहीं किया हैDrive
, तो सिस्टम को यह पहचानने का कोई तरीका नहीं होगा कि आभासी Car.Drive
क्या करेंगे। उपरोक्त italicized स्थिति से बचने के लिए इंटरफेस इस समस्या से बचते हैं।
void UseCar(Car &foo)
; बहुविकल्पी के बीच शामिल करने के लिए की उम्मीद नहीं की जा सकती है ToyotaCar::Drive
और HybridCar::Drive
(है कि उन अन्य प्रकार के भी, क्योंकि यह अक्सर न पता होना चाहिए और न ही देखभाल मौजूद )। C ++ करता है, एक भाषा को उस कोड की आवश्यकता होती है, जिसे ToyotaCar &myCar
पास करने की इच्छा के साथ उस कोड UseCar
को पहले HybridCar
या तो डाली जानी चाहिए ToyotaCar
, लेकिन ((कार) (HybridCar) myCar) .Drive` और ((Car)(ToyotaCar)myCar).Drive
अलग-अलग चीजें करेगा, जो कि अपस्टेक होगा। पहचान-संरक्षण नहीं थे।
आप एकाधिक विरासत के बारे में oracle प्रलेखन पृष्ठ में इस क्वेरी के लिए सटीक उत्तर पा सकते हैं
राज्य की कई विरासत: कई वर्गों से खेतों को प्राप्त करने की क्षमता
जावा प्रोग्रामिंग भाषा आपको एक से अधिक वर्ग का विस्तार करने की अनुमति नहीं देती है, इसका कारण राज्य की कई विरासतों के मुद्दों से बचना है, जो कि कई वर्गों से खेतों को विरासत में लेने की क्षमता है।
यदि एकाधिक वंशानुक्रम की अनुमति दी जाती है और जब आप उस वर्ग को तात्कालिक करके किसी वस्तु का निर्माण करते हैं, तो वह वस्तु वर्ग के सभी सुपरक्लेसेस के क्षेत्रों से विरासत में प्राप्त होगी। यह दो मुद्दों का कारण होगा।
कार्यान्वयन का एकाधिक वंशानुक्रम: कई वर्गों से विधि परिभाषाएँ प्राप्त करने की क्षमता
इस दृष्टिकोण के साथ समस्याएं: नाम ts और अस्पष्टता । यदि एक उपवर्ग और सुपरक्लास में समान विधि नाम (और हस्ताक्षर) होते हैं, तो संकलक यह निर्धारित नहीं कर सकता कि किस संस्करण को लागू करना है।
लेकिन जावा डिफ़ॉल्ट तरीकों के साथ कई प्रकार की विरासत का समर्थन करता है , जिसे जावा 8 रिलीज के बाद से पेश किया गया है। जावा कंपाइलर यह निर्धारित करने के लिए कुछ नियम प्रदान करता है कि कोई विशेष वर्ग किस डिफ़ॉल्ट विधि का उपयोग करता है।
हीरे की समस्या को हल करने के बारे में अधिक जानकारी के लिए एसई पोस्ट के नीचे देखें:
एक से अधिक वंशानुक्रम: एक से अधिक इंटरफ़ेस को लागू करने के लिए एक वर्ग की क्षमता।
चूँकि इंटरफ़ेस में परिवर्तनशील क्षेत्र नहीं होते हैं, इसलिए आपको उन समस्याओं के बारे में चिंता करने की ज़रूरत नहीं है जो यहाँ राज्य के कई उत्तराधिकार से उत्पन्न होती हैं।
यह कहा जाता है कि वस्तुओं के राज्य को इसमें खेतों के संबंध में संदर्भित किया जाता है और यह बहुत अस्पष्ट हो जाएगा यदि बहुत सारी कक्षाएं विरासत में मिलीं। लिंक यहां दिया गया है
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
जावा केवल इंटरफेस के माध्यम से कई विरासत का समर्थन करता है। एक वर्ग किसी भी संख्या में इंटरफेस को लागू कर सकता है लेकिन केवल एक वर्ग का विस्तार कर सकता है।
एकाधिक वंशानुक्रम का समर्थन नहीं किया जाता है क्योंकि यह घातक हीरे की समस्या की ओर जाता है। हालाँकि, इसे हल किया जा सकता है, लेकिन यह जटिल प्रणाली की ओर जाता है इसलिए जावा संस्थापकों द्वारा कई विरासत को गिरा दिया गया है।
फरवरी १ ९९ ५ में जेम्स गोस्लिंग द्वारा "जावा: ए ओवरव्यू" नामक श्वेत पत्र में ( लिंक ) एक विचार देता है कि जावा में एकाधिक वंशानुक्रम का समर्थन क्यों नहीं किया गया है।
गोस्लिंग के अनुसार:
"JAVA ने C ++ की कई भ्रामक, खराब समझी जाने वाली भ्रामक विशेषताओं को छोड़ दिया है, जो हमारे अनुभव में bene b t की तुलना में अधिक दु: ख पहुंचाती हैं। इसमें मुख्य रूप से ऑपरेटर ओवरलोडिंग (हालांकि इसमें ओवरलोडिंग का तरीका होता है), मल्टीपल इनहेरेंडेंस, और व्यापक ऑटोमैटिक कॉर्शन होते हैं।"
उसी कारण से C # एकाधिक उत्तराधिकार की अनुमति नहीं देता है लेकिन आपको कई इंटरफ़ेस लागू करने की अनुमति देता है।
C ++ w / multiple inheritence से सीखा गया पाठ यह था कि यह मूल्य की तुलना में अधिक मुद्दों को जन्म देता है।
एक इंटरफ़ेस चीजों का एक अनुबंध है जिसे आपकी कक्षा को लागू करना है। आप इंटरफ़ेस से कोई कार्यक्षमता हासिल नहीं करते हैं। वंशानुक्रम आपको एक मूल वर्ग (और कई-विरासत में, जो बेहद भ्रमित कर सकता है) की कार्यक्षमता को प्राप्त करने की अनुमति देता है।
कई इंटरफेस की अनुमति देने से आप एक ही प्रकार के मुद्दों को हल करने के लिए डिज़ाइन पैटर्न (एडेप्टर की तरह) का उपयोग कर सकते हैं जो आप कई उत्तराधिकार का उपयोग करके हल कर सकते हैं, लेकिन बहुत अधिक विश्वसनीय और अनुमानित तरीके से।
D1
और D2
से दोनों इनहेरिट B
, और प्रत्येक ओवरराइड एक समारोह f
है, और अगर obj
एक प्रकार का एक उदाहरण है S
जो दोनों से विरासत में मिली D1
और D2
लेकिन हावी नहीं होता f
है, तो एक संदर्भ के लिए कास्टिंग S
के लिए D1
कुछ जिसका उपज चाहिए f
का उपयोग करता है D1
ओवरराइड, और करने के लिए कास्टिंग B
नहीं करना चाहिए उसे बदलो। इसी तरह एक संदर्भ कास्टिंग S
के लिए D2
एक कुछ जिसका उपज चाहिए f
का उपयोग करता है D2
ओवरराइड, और करने के लिए कास्टिंग B
कि परिवर्तन नहीं होना चाहिए। यदि किसी भाषा को आभासी सदस्यों को जोड़ने की अनुमति देने की आवश्यकता नहीं है ...
चूंकि यह विषय बंद नहीं है, इसलिए मैं इस उत्तर को पोस्ट करूंगा, मुझे आशा है कि इससे किसी को यह समझने में मदद मिलेगी कि जावा एकाधिक वंशानुक्रम की अनुमति क्यों नहीं देता है।
निम्नलिखित वर्ग पर विचार करें:
public class Abc{
public void doSomething(){
}
}
इस मामले में वर्ग एबीसी सही कुछ भी नहीं बढ़ाता है? इतनी जल्दी नहीं, इस वर्ग के निहितार्थ से क्लास ऑब्जेक्ट, बेस क्लास का विस्तार होता है जो जावा में सब कुछ काम करने की अनुमति देता है। सब कुछ एक वस्तु है।
आप वर्ग का उपयोग करने के ऊपर आप देखेंगे कि आपके आईडीई तुम जैसे तरीकों का उपयोग करने की अनुमति की कोशिश करते हैं: equals(Object o)
, toString()
, आदि, लेकिन आप उन तरीकों की घोषणा नहीं किया, वे आधार वर्ग से आयाObject
तुम कोशिश कर सकते हो:
public class Abc extends String{
public void doSomething(){
}
}
यह ठीक है, क्योंकि आपका वर्ग निहित नहीं होगा, Object
लेकिन विस्तारित होगा String
क्योंकि आपने कहा था। निम्नलिखित बदलाव पर विचार करें:
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
अब आपकी कक्षा हमेशा "हैलो" लौटाएगी यदि आप स्ट्रींग () को कॉल करते हैं।
अब निम्न वर्ग की कल्पना करें:
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
फिर से वर्ग Flyer
निहित वस्तु का विस्तार होता है जो विधि है toString()
, किसी भी वर्ग के पास यह विधि होगी क्योंकि वे सभी Object
अप्रत्यक्ष रूप से विस्तारित होते हैं , इसलिए, यदि आप कॉल करते toString()
हैं Bird
, तो किस toString()
जावा का उपयोग करना होगा? से Abc
या Flyer
? यह किसी भी वर्ग के साथ होगा जो दो या अधिक वर्गों को विस्तारित करने का प्रयास करता है, इस तरह की "विधि टक्कर" से बचने के लिए उन्होंने इंटरफ़ेस का विचार बनाया , मूल रूप से आप उन्हें एक सार वर्ग के रूप में सोच सकते हैं जो परोक्ष रूप से ऑब्जेक्ट का विस्तार नहीं करता है । चूंकि वे अमूर्त हैं, इसलिए उन्हें एक वर्ग द्वारा लागू करना होगा, जो एक वस्तु है (आप अकेले एक इंटरफेस नहीं बना सकते हैं, उन्हें एक वर्ग द्वारा लागू किया जाना चाहिए), इसलिए सब कुछ ठीक काम करना जारी रखेगा।
इंटरफेस से कक्षाओं को अलग करने के लिए, कीवर्ड इंप्लाइमेंट केवल इंटरफेस के लिए आरक्षित किया गया था।
आप अपनी पसंद के किसी भी इंटरफ़ेस को एक ही कक्षा में लागू कर सकते हैं क्योंकि वे डिफ़ॉल्ट रूप से कुछ भी नहीं बढ़ाते हैं (लेकिन आप एक इंटरफ़ेस बना सकते हैं जो दूसरे इंटरफ़ेस का विस्तार करता है, लेकिन फिर से, "पिता" इंटरफ़ेस ऑब्जेक्ट नहीं बढ़ाएगा "), इसलिए एक इंटरफ़ेस है बस एक इंटरफ़ेस और वे " विधियों हस्ताक्षर कॉलिशन " से पीड़ित नहीं होंगे , यदि वे करते हैं तो कंपाइलर आपको चेतावनी देगा और आपको इसे ठीक करने के लिए विधि हस्ताक्षर को बदलना होगा (हस्ताक्षर = विधि का नाम + params + वापसी प्रकार) ।
public interface Flyer{
public void makeFly(); // <- method without implementation
}
public class Bird extends Abc implements Flyer{
public void doAnotherThing(){
}
@Override
public void makeFly(){ // <- implementation of Flyer interface
}
// Flyer does not have toString() method or any method from class Object,
// no method signature collision will happen here
}
क्योंकि एक इंटरफ़ेस सिर्फ एक अनुबंध है। और एक वर्ग वास्तव में डेटा के लिए एक कंटेनर है।
उदाहरण के लिए दो वर्ग A, B में एक ही विधि m1 () है। और कक्षा C, A, B दोनों का विस्तार करता है।
class C extends A, B // for explaining purpose.
अब, वर्ग C m1 की परिभाषा खोजेगा। सबसे पहले, यह कक्षा में खोज करेगा यदि यह नहीं मिला तो यह माता-पिता की कक्षा में जाँच करेगा। दोनों A, B की परिभाषा है इसलिए यहाँ अस्पष्टता होती है कि कौन सी परिभाषा चुननी चाहिए। तो जावेद ने बहुउद्देश्यीय समर्थन नहीं किया।
जावा दो कारणों से एकाधिक वंशानुक्रम का समर्थन नहीं करता है:
Object
। जब यह एक से अधिक सुपर क्लास से विरासत में मिलता है, तो सब क्लास को ऑब्जेक्ट क्लास की संपत्ति हासिल करने की अस्पष्टता मिलती है।super()
समर्थक वर्ग निर्माता को आह्वान करने के लिए बुला रहा है। यदि कक्षा में एक से अधिक सुपर क्लास हैं, तो यह भ्रमित हो जाता है।इसलिए जब एक वर्ग एक से अधिक सुपर क्लास से निकलता है, तो हमें संकलन समय त्रुटि मिलती है।
उदाहरण के लिए उदाहरण लीजिए कि क्लास ए में गेटसमॉर्टिंग मेथड है और क्लास बी में गेट्सोमेटिंग मेथड है और क्लास सी में ए और बी का विस्तार होता है। अगर कोई सी। सॉटसोमेटिंग कहलाता है तो क्या होगा? यह निर्धारित करने का कोई तरीका नहीं है कि किस पद्धति को कॉल करना है।
मूल रूप से इंटरफेस केवल यह निर्दिष्ट करता है कि एक कार्यान्वयन वर्ग को किन तरीकों की आवश्यकता है। एक वर्ग जो कई इंटरफेस को लागू करता है, इसका मतलब है कि कक्षा को उन सभी इंटरफेस से तरीकों को लागू करना है। जैसा कि ऊपर वर्णित किसी भी मुद्दे पर कोई भी नहीं करेगा।
एक परिदृश्य पर विचार करें जहां टेस्ट 1, टेस्ट 2 और टेस्ट 3 तीन कक्षाएं हैं। टेस्ट 3 वर्ग को टेस्ट 2 और टेस्ट 1 कक्षाएं विरासत में मिली हैं। यदि Test1 और Test2 वर्गों में समान विधि है और आप इसे चाइल्ड क्लास ऑब्जेक्ट से कहते हैं, तो Test1 या Test2 वर्ग की कॉल विधि के लिए अस्पष्टता होगी, लेकिन इंटरफ़ेस के लिए ऐसी कोई अस्पष्टता नहीं है क्योंकि इंटरफ़ेस में कोई कार्यान्वयन नहीं है।
अस्पष्टता समस्या के कारण जावा बहु विरासत, बहुपथ और संकर विरासत का समर्थन नहीं करता है:
Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ? So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.
संदर्भ लिंक: https://plus.google.com/u/0/communities/102217496457095083679
सरल तरीके से हम सभी जानते हैं, हम एक वर्ग का उत्तराधिकार (विस्तार) कर सकते हैं, लेकिन हम बहुत सारे इंटरफेस को लागू कर सकते हैं .. ऐसा इसलिए है क्योंकि इंटरफेस में हम एक कार्यान्वयन नहीं देते हैं बस कार्यक्षमता कहते हैं। मान लीजिये कि जावा इतने सारे वर्गों का विस्तार कर सकती है और उनके पास एक ही तरीके हैं .. इस बिंदु में अगर हम सब क्लास में सुपर क्लास पद्धति को लागू करने का प्रयास करते हैं तो क्या विधि को चलाने के लिए लगता है ??, संकलक को भ्रमित उदाहरण मिलता है : - एकाधिक एक्सटेंशन्स के लिए प्रयास करें लेकिन उन तरीकों को इंटरफेस करता है जिनके पास निकाय नहीं हैं जिन्हें हमें उप श्रेणी में लागू करना चाहिए .. कई लागू करने की कोशिश करें ताकि कोई चिंता न हो।
* यह एक सरल उत्तर है क्योंकि मैं जावा में एक शुरुआत हूँ *
विचार करें कि तीन वर्ग हैं X
, Y
और Z
।
तो हम जैसे विरासत में कर रहे हैं X extends Y, Z
और दोनों Y
और Z
एक विधि चल रहा है alphabet()
एक ही वापसी प्रकार और तर्क के साथ। इस विधि alphabet()
में Y
करने के लिए कहते हैं पहले वर्णमाला प्रदर्शित और विधि वर्णमाला में Z
कहते हैं प्रदर्शन पिछले वर्णमाला । तो यहाँ जब alphabet()
बुलाया जाता है तो अस्पष्टता आती है X
। चाहे वह पहले या अंतिम वर्णमाला को प्रदर्शित करने के लिए कहता है ??? इसलिए जावा मल्टीपल इनहेरिटेंस का समर्थन नहीं कर रहा है। इंटरफेस के मामले में, विचार करें Y
और Z
इंटरफेस के रूप में। तो दोनों में विधि की घोषणा शामिल होगी alphabet()
लेकिन परिभाषा नहीं। यह नहीं बताएगा कि पहले वर्णमाला या अंतिम वर्णमाला या कुछ भी प्रदर्शित करना है लेकिन सिर्फ एक विधि की घोषणा करेंगेalphabet()
। इसलिए अस्पष्टता बढ़ाने का कोई कारण नहीं है। हम कक्षा के अंदर जो कुछ भी चाहते हैं उसके साथ विधि को परिभाषित कर सकते हैं X
।
तो एक शब्द में, इंटरफेस में परिभाषा को लागू करने के बाद किया जाता है ताकि कोई भ्रम न हो।