क्या कोई उदाहरण अधिक विशिष्ट प्रकार के कुछ अन्य उदाहरण के बराबर हो सकता है?


25

मैंने इस लेख को पढ़ा है: जावा में एक समानता विधि कैसे लिखें

मूल रूप से, यह एक समान () विधि के लिए एक समाधान प्रदान करता है जो विरासत का समर्थन करता है:

Point2D twoD   = new Point2D(10, 20);
Point3D threeD = new Point3D(10, 20, 50);
twoD.equals(threeD); // true
threeD.equals(twoD); // true

लेकिन क्या यह एक अच्छा विचार है? ये दो उदाहरण समान प्रतीत होते हैं लेकिन दो अलग-अलग हैश कोड हो सकते हैं। क्या यह गलत नहीं है?

मेरा मानना ​​है कि इसके बजाय ऑपरेटर्स को कास्ट करने से यह बेहतर होगा।


1
लिंक में दिए गए रंगीन बिंदुओं के साथ उदाहरण मेरे लिए अधिक मायने रखता है। मैं इस बात पर विचार करूंगा कि एक 2D बिंदु (x, y) को एक शून्य Z घटक (x, y, 0) के साथ 3D बिंदु के रूप में देखा जा सकता है, और मैं चाहूंगा कि समानता आपके मामले में झूठी लौटे। वास्तव में, लेख में, एक ColoredPoint स्पष्ट रूप से एक बिंदु से अलग होने के लिए कहा जाता है और हमेशा झूठ बोलता है।
coredump

10
सामान्य सम्मेलनों को तोड़ने वाले ट्यूटोरियल से बदतर कुछ भी नहीं ... प्रोग्रामर के बाहर उन प्रकार की आदतों को तोड़ने में वर्षों लगते हैं।
corsiKa

3
@coredump एक शून्य बिंदु के रूप में 2 डी बिंदु का इलाज zकरना कुछ अनुप्रयोगों के लिए एक उपयोगी सम्मेलन हो सकता है (विरासत डेटा को संभालने वाले प्रारंभिक सीएडी सिस्टम दिमाग में आते हैं)। लेकिन यह एक मनमाना सम्मेलन है। 3 या अधिक आयामों वाले रिक्त स्थान में मनमाना झुकाव हो सकता है ... यह वही है जो दिलचस्प समस्याओं को दिलचस्प बनाता है।
3

जवाबों:


71

यह समानता नहीं होनी चाहिए क्योंकि यह सकारात्मकता को तोड़ती है । इन दो भावों पर विचार करें:

new Point3D(10, 20, 50).equals(new Point2D(10, 20)) // true
new Point2D(10, 20).equals(new Point3D(10, 20, 60)) // true

चूंकि समानता सकर्मक है, इसका मतलब यह होना चाहिए कि निम्नलिखित अभिव्यक्ति भी सत्य है:

new Point3D(10, 20, 50).equals(new Point3D(10, 20, 60))

लेकिन निश्चित रूप से - यह नहीं है।

इसलिए, कास्टिंग का आपका विचार सही है - उम्मीद करें कि जावा में, कास्टिंग का अर्थ केवल संदर्भ के प्रकार को कास्टिंग करना है। आप वास्तव में यहां क्या चाहते हैं एक रूपांतरण विधि है जो एक Point2Dवस्तु से एक नई वस्तु बनाएगी Point3D। इससे अभिव्यक्ति और भी सार्थक होगी:

twoD.equals(threeD.projectXY())

1
लेख में उन कार्यान्वयनों का वर्णन किया गया है जो परिवर्तनशीलता को तोड़ते हैं और कई तरह के काम-के बारे में बताते हैं। एक डोमेन में जहां हम 2D पॉइंट्स की अनुमति देते हैं, हमने पहले ही तय कर लिया है कि तीसरा आयाम मायने नहीं रखता। और इसलिए (10, 20, 50)बराबरी (10, 20, 60)ठीक है। हम केवल के बारे में परवाह 10है और 20
17

1
खुद का प्रतिनिधित्व प्रदान करने के लिए Point2Dएक projectXYZ()विधि होनी चाहिए Point3D? दूसरे शब्दों में, क्या कार्यान्वयन को एक दूसरे को जानना चाहिए ?
hjk

4
@hjk से छुटकारा Point2Dसरल लग रहा है क्योंकि 2 डी बिंदुओं को प्रोजेक्ट करने के लिए पहले उनके विमान को 3 डी स्पेस में परिभाषित करने की आवश्यकता होती है। यदि 2 डी बिंदु जानता है कि यह विमान है, तो यह पहले से ही एक 3 डी बिंदु है। यदि ऐसा नहीं होता है, तो यह परियोजना नहीं कर सकता है। मैं एबट के फ्लैटलैंड की याद दिला रहा हूं ।
बेन रूडर्स 3

@benrudgers आप किसी Plane3Dवस्तु को परिभाषित कर सकते हैं, जो कि 3 डी अंतरिक्ष में एक विमान को परिभाषित करेगा, उस विमान में एक liftविधि हो सकती है (2 डी-> 3 डी लिफ्टिंग है, प्रोजेक्ट नहीं है) जो Point2D"तीसरी धुरी के लिए एक संख्या और एक संख्या को स्वीकार करेगा। ”- विमान से विमान की दूरी सामान्य। उपयोग में आसानी के लिए, आप आम विमानों को स्थिर स्थिरांक के रूप में परिभाषित कर सकते हैं, इसलिए आप ऐसा कर सकते हैंPlane3D.XY.lift(new Point2D(10, 20), 50).equals(new Point3D(10, 20, 50))
Idan Arye

@IdanArye मैं इस सुझाव पर टिप्पणी कर रहा था कि 2 डी बिंदुओं में एक प्रक्षेपण विधि होनी चाहिए। लिफ्ट के तरीकों के साथ विमानों के रूप में, मुझे लगता है कि यह समझ बनाने के लिए दो तर्क की आवश्यकता होगी: एक 2 डी बिंदु और जिस विमान को यह माना जाता है, यानी अगर यह बिंदु नहीं है तो इसे वास्तव में एक प्रक्षेपण की आवश्यकता है ... और अगर यह बिंदु का मालिक है, तो क्यों न केवल एक 3 डी बिंदु का मालिक है और एक समस्याग्रस्त डेटा प्रकार और एक कीलिंग विधि की गंध के साथ दूर है? YMMV।
बेन रडर्स

10

मैं एलन जे। पर्लिस के ज्ञान के बारे में सोचते हुए लेख पढ़ने से दूर रहा :

एपिग्राम 9. 10 डेटा संरचनाओं पर 10 कार्यों की तुलना में एक डेटा संरचना पर 100 फ़ंक्शन संचालित करना बेहतर है।

यह तथ्य कि "समानता" अधिकार प्राप्त करना समस्या का एक प्रकार है जो रात में स्काला के मार्टिन ऑर्डर्सकी आविष्कारक को बनाए रखता है , इस बारे में विराम देना चाहिए कि equalsविरासत के पेड़ में ओवरराइड करना एक अच्छा विचार है या नहीं।

जब हम अशुभ होते हैं, तो ऐसा होता ColoredPointहै कि हमारी ज्यामिति विफल हो जाती है क्योंकि हमने एक अच्छा बनाने के बजाय डेटा प्रकारों के प्रसार के लिए उपयोग किया है। यह वापस जाने और equalsकाम करने के लिए वंशानुक्रम वृक्ष की जड़ नोड को संशोधित करने के बावजूद । सिर्फ zऔर सिर्फ एक colorको ही क्यों न जोड़ें Point?

अच्छा कारण यह है कि विभिन्न डोमेन में काम करेंगे Pointऔर ColoredPointसंचालित करेंगे ... कम से कम यदि उन डोमेन को कभी नहीं मिलाया जाए। फिर भी अगर ऐसा है, तो हमें ओवरराइड करने की जरूरत नहीं है equals। तुलना ColoredPointऔर Pointसमानता के लिए केवल एक तीसरे डोमेन में समझ में आता है, जहां उन्हें मिलाने की अनुमति है। और उस स्थिति में, संभवत: एक या दूसरे से या दोनों के बिना एक समान शब्दावलियों को लागू करने की कोशिश करने के बजाय उस तीसरे डोमेन के अनुरूप "समानता" होना बेहतर है। दूसरे शब्दों में "समानता" को उस स्थान पर स्थानीय रूप से परिभाषित किया जाना चाहिए जहाँ हमें दोनों तरफ से मिट्टी बह रही है क्योंकि हम ColoredPoint.equals(pt)उदाहरणों के खिलाफ असफल नहीं होना चाह सकते हैं, Pointभले ही लेखक का ColoredPointयह विचार 6 महीने पहले 2am पर एक अच्छा विचार था। ।


6

जब पुराने प्रोग्रामिंग देवता कक्षाओं के साथ ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग का आविष्कार कर रहे थे, तो उन्होंने फैसला किया कि जब यह रचना और विरासत के लिए एक वस्तु के लिए दो संबंध रखता है: "एक" और "एक" है।
इसने अभिभावकों की तुलना में उपवर्गों की समस्या को आंशिक रूप से हल किया, लेकिन बिना कोड को तोड़े उन्हें उपयोगी बना दिया। क्योंकि एक उपवर्ग उदाहरण एक "सुपरक्लास ऑब्जेक्ट" है और इसके लिए सीधे प्रतिस्थापित किया जा सकता है, भले ही उपवर्ग में अधिक सदस्य फ़ंक्शन या डेटा सदस्य हों, "एक" की गारंटी है यह माता-पिता के सभी कार्यों को पूरा करेगा और इसके सभी सदस्य हैं। तो आप कह सकते हैं कि एक Point3D "एक बिंदु है, और एक Point2D" एक "बिंदु है अगर वे दोनों बिंदु से विरासत में मिले हैं। इसके अतिरिक्त एक प्वाइंट 3 डी, प्वाइंट 2 डी का एक उपवर्ग हो सकता है।

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

तो आपको समानताओं की एक तालिका मिलती है:

Both objects have same values, limited to subset of shared members

Child classes can be equal to parent classes if parent and childs
data members are the same.

Both objects entire data members are the same.

Objects must have all same values and be similar classes. 

Objects must have all same values and be the same class type. 

Equality is determined by specific logical conditions in the domain.

Only Objects that both point to same instance are equal. 

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

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


2

नियम है, जब भी आप ओवरराइड करते हैं hashcode(), आप ओवरराइड करते हैं equals(), और इसके विपरीत। यह एक अच्छा विचार है या नहीं यह इच्छित उपयोग पर निर्भर करता है। व्यक्तिगत रूप से, मैं एक isLike()ही प्रभाव प्राप्त करने के लिए एक अलग विधि ( या समान) के साथ जाऊंगा ।


1
हैशकोड को ओवरराइड किए बिना समतुल्य करना ठीक हो सकता है। उदाहरण के लिए, एक ही समानता की स्थिति के लिए एक अलग हैशिंग एल्गोरिथ्म का परीक्षण करने के लिए ऐसा करेगा।
पेट्रीसिया शहनहान

1

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

उदाहरण के लिए, doubleमूल्यों के एक अपरिवर्तनीय 2D मैट्रिक्स को घेरने वाले वर्ग पर विचार करें । यदि कोई बाहर की विधि 1000 के एक पहचान मैट्रिक्स के लिए पूछती है, तो दूसरा एक विकर्ण मैट्रिक्स के लिए पूछता है और एक सरणी देता है जिसमें 1000 लोग होते हैं, और तीसरा एक 2D मैट्रिक्स के लिए पूछता है और एक 1000x1000 सरणी पास करता है जहां प्राथमिक विकर्ण पर तत्व सभी 1.0 हैं और अन्य सभी शून्य हैं, सभी तीनों वर्गों को दी गई वस्तुएं अलग-अलग बैकिंग स्टोर्स का आंतरिक रूप से उपयोग कर सकती हैं [पहला आकार के लिए एक एकल क्षेत्र, दूसरा एक हजार-तत्व सरणी, और तीसरा एक हजार 1000-तत्व सरणियाँ] लेकिन एक दूसरे के समतुल्य के रूप में रिपोर्ट करना चाहिए [क्योंकि तीनों एक 1000x1000 अपरिवर्तनीय मैट्रिक्स को विकर्ण और हर जगह शून्य पर लोगों के साथ संलग्न करते हैं]।

इस तथ्य से परे कि यह अलग-अलग बैकिंग-स्टोर प्रकारों के अस्तित्व को छुपाता है, रैपर तुलना की सुविधा के लिए भी उपयोगी होगा, क्योंकि तुल्यता के लिए आइटम की जांच करना आमतौर पर एक बहु-चरण प्रक्रिया होगी। पहले आइटम से पूछें कि क्या यह जानता है कि क्या यह दूसरे के बराबर है; यदि यह नहीं जानता है, तो दूसरे से पूछें कि क्या यह जानता है कि क्या यह पहले के बराबर है। यदि न तो ऑब्जेक्ट पता है, तो प्रत्येक सरणी को उसके व्यक्तिगत तत्वों की सामग्री के बारे में पूछें [कोई व्यक्ति लंबी-धीमी व्यक्तिगत-आइटम तुलना मार्ग का निर्णय लेने से पहले अन्य चेक जोड़ सकता है]।

ध्यान दें कि इस परिदृश्य में प्रत्येक ऑब्जेक्ट के लिए समतुल्यता-परीक्षण विधि को तीन-राज्य मान ("हाँ मैं समकक्ष", "नहीं, मैं समकक्ष नहीं हूँ", या "मुझे नहीं पता") को वापस करने की आवश्यकता होगी, इसलिए सामान्य "बराबरी" विधि उपयुक्त नहीं होगी। जब कोई भी वस्तु किसी अन्य के बारे में पूछे जाने पर "मुझे नहीं पता" का जवाब दे सकती है, तो तर्क को उदाहरण के लिए एक विकर्ण मैट्रिक्स में जोड़ा जा सकता है जो किसी भी पहचान मैट्रिक्स या विकर्ण मैट्रिक्स को मुख्य विकर्ण से किसी भी तत्व के बारे में पूछने में परेशान नहीं करेगा। प्रकार के।

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