किलियन फोथ का जवाब बेहतरीन है। मैं सिर्फ कैनोनिकल उदाहरण जोड़ना चाहूंगा कि यह एक समस्या क्यों है। एक पूर्णांक बिंदु वर्ग की कल्पना करें:
class Point2D {
public int x;
public int y;
// constructor
public Point2D(int theX, int theY) { x = theX; y = theY; }
public int hashCode() { return x + y; }
public boolean equals(Object o) {
if (this == o) { return true; }
if ( !(o instanceof Point2D) ) { return false; }
Point2D that = (Point2D) o;
return (x == that.x) &&
(y == that.y);
}
}
अब इसे 3 डी पॉइंट होने के लिए सब-क्लास करें।
class Point3D extends Point2D {
public int z;
// constructor
public Point3D(int theX, int theY, int theZ) {
super(x, y); z = theZ;
}
public int hashCode() { return super.hashCode() + z; }
public boolean equals(Object o) {
if (this == o) { return true; }
if ( !(o instanceof Point3D) ) { return false; }
Point3D that = (Point3D) o;
return super.equals(that) &&
(z == that.z);
}
}
सुपर सरल! चलो हमारे अंक का उपयोग करें:
Point2D p2a = new Point2D(3, 5);
Point2D p2b = new Point2D(3, 5);
Point2D p2c = new Point2D(3, 7);
p2a.equals(p2b); // true
p2b.equals(p2a); // true
p2a.equals(p2c); // false
Point3D p3a = new Point3D(3, 5, 7);
Point3D p3b = new Point3D(3, 5, 7);
Point3D p3c = new Point3D(3, 7, 11);
p3a.equals(p3b); // true
p3b.equals(p3a); // true
p3a.equals(p3c); // false
आप शायद सोच रहे हैं कि मैं इतना आसान उदाहरण क्यों पोस्ट कर रहा हूं। यहाँ पकड़ है:
p2a.equals(p3a); // true
p3a.equals(p2a); // FALSE!
जब हम 2D बिंदु की तुलना बराबर 3D बिंदु से करते हैं, तो हम सही हो जाते हैं, लेकिन जब हम तुलना को उल्टा करते हैं, तो हम झूठे हो जाते हैं (क्योंकि पी 2 ए विफल हो जाता है instanceof Point3D
)।
निष्कर्ष
यह आमतौर पर एक उपवर्ग में इस तरह से लागू करने के लिए संभव है कि यह अब-के साथ संगत नहीं है कि सुपर-क्लास इसे कैसे काम करने की उम्मीद करता है।
समान रूप से एक भिन्न उपवर्ग पर समान (समान) को लागू करना असंभव है, जो कि अभिभावक वर्ग के अनुकूल है।
जब आप एक वर्ग लिखते हैं जिसे आप लोगों को उपवर्ग के लिए अनुमति देने का इरादा रखते हैं, तो प्रत्येक विधि का व्यवहार कैसे होना चाहिए, इसके लिए एक अनुबंध लिखना बहुत अच्छा विचार है । इससे भी बेहतर होगा कि यूनिट परीक्षणों का एक सेट जो लोग ओवरराइड विधियों के अपने कार्यान्वयन के खिलाफ चला सकते हैं ताकि यह साबित हो सके कि वे अनुबंध का उल्लंघन नहीं करते हैं। लगभग कोई भी ऐसा नहीं करता है क्योंकि यह बहुत अधिक काम है। लेकिन अगर आप परवाह करते हैं, तो यह करने की बात है।
एक अच्छी तरह से वर्तनी अनुबंध का एक बढ़िया उदाहरण है तुलना । .equals()
ऊपर बताए गए कारणों के बारे में जो कहता है, उसे अनदेखा करें । यहाँ एक उदाहरण है कि कैसे तुलनित्र चीजों .equals()
को नहीं कर सकता है ।
टिप्पणियाँ
जोश बलोच का "प्रभावी जावा" आइटम 8 इस उदाहरण का स्रोत था, लेकिन बलोच एक ColorPoint का उपयोग करता है जो तीसरे अक्ष के बजाय एक रंग जोड़ता है और चींटियों के बजाय युगल का उपयोग करता है। बलोच का जावा उदाहरण मूल रूप से ओडस्की / स्पून / वेनर्स द्वारा दोहराया गया है जिन्होंने अपना उदाहरण ऑनलाइन उपलब्ध कराया है।
इस उदाहरण पर कई लोगों ने आपत्ति जताई है क्योंकि अगर आपने अभिभावक वर्ग को उप-वर्ग के बारे में बताया है, तो आप इस समस्या को ठीक कर सकते हैं। यह सच है अगर वहाँ पर्याप्त संख्या में उप-कक्षाएं हैं और अगर माता-पिता को उन सभी के बारे में पता है। लेकिन मूल प्रश्न एक एपीआई बनाने के बारे में था जिसके लिए कोई और उप-वर्ग लिखेगा। उस स्थिति में, आप आमतौर पर उप-वर्गों के साथ संगत होने के लिए मूल कार्यान्वयन को अपडेट नहीं कर सकते हैं।
बोनस
तुलनित्र भी दिलचस्प है क्योंकि यह समान रूप से () को लागू करने के मुद्दे के आसपास काम करता है। बेहतर अभी तक, यह इस प्रकार की विरासत के मुद्दे को ठीक करने के लिए एक पैटर्न का अनुसरण करता है: रणनीति डिजाइन पैटर्न। हास्केल और स्काला के लोग जिस टाइपकालेज़ के बारे में बताते हैं, वह स्ट्रेटेजी पैटर्न भी है। विरासत बुरा या गलत नहीं है, यह सिर्फ मुश्किल है। आगे पढ़ने के लिए, फिलिप वाडलर के पेपर देखें कि कैसे तदर्थ बहुरूपता कम तदर्थ बनाया जाए