2017-01-25 8 views
5

मैं एक परियोजना जहां कई वर्गों equals और hashCode के समुचित ठेठ कार्यान्वयन की जरूरत पर काम कर रहा हूँ की पुन: प्रयोज्य कार्यान्वयन: प्रत्येक वर्ग ("गहरा" अपरिवर्तनीय वस्तुओं के साथ निर्माण पर प्रारंभ अंतिम क्षेत्रों का एक सेट null रों करना है है कुछ मामलों में स्वीकार किया जाना है) हैशिंग और तुलना के लिए इस्तेमाल किया जाना चाहिए।बराबरी और hashCode

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

public abstract class AbstractHashable { 

    /** List of fields used for comparison. */ 
    private final Object[] fields; 

    /** Precomputed hash. */ 
    private final int hash; 

    /** 
    * Constructor to be invoked by subclasses. 
    * @param fields list of fields used for comparison between objects of this 
    * class, they must be in constant number for each class 
    */ 
    protected AbstractHashable(Object... fields) { 
     this.fields = fields; 
     hash = 31 * getClass().hashCode() + Objects.hash(fields); 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (obj == this) { 
      return true; 
     } 
     if (obj == null || !getClass().equals(obj.getClass())) { 
      return false; 
     } 
     AbstractHashable other = (AbstractHashable) obj; 
     if (fields.length != other.fields.length) { 
      throw new UnsupportedOperationException(
        "objects of same class must have the same number of fields"); 
     } 
     for (int i=0; i<fields.length; i++) { 
      if (!fields[i].equals(other.fields[i])) { 
       return false; 
      } 
     } 
     return true; 
    } 

    @Override 
    public int hashCode() { 
     return hash; 
    } 

} 

यह इस तरह से इस्तेमाल किया जा करने का इरादा है:

public class SomeObject extends AbstractHashable { 

    // both Foo and Bar have no mutable state 
    private final Foo foo; 
    private final Bar bar; 

    public SomeObject(Foo foo, Bar bar) { 
     super(foo, bar); 
     this.foo = Objects.requireNonNull(foo); 
     this.bar = bar; // null supported 
    } 

    // other methods, no equals or hashCode needed 

} 

यह मूलतः क्या कुछ मतभेद के साथ here प्रस्तावित है।

यह मुझे वर्बोसिटी को कम करने के लिए एक सीधा लेकिन अच्छा दृष्टिकोण प्रतीत होता है और अभी भी equals और hashCode के कुशल कार्यान्वयन हैं। हालांकि, जैसा कि मैंने कभी कुछ ऐसा नहीं देखा है (ऊपर दिए गए उत्तर को छोड़कर), मैं विशेष रूप से पूछना चाहता हूं कि आवेदन करने से पहले इस दृष्टिकोण (या संभवतः कुछ सुधार जो लागू किया जा सकता है) के खिलाफ कुछ बिंदु है या नहीं पूरे परियोजना में यह।

+1

मुझे पहले से ही दो मुद्दे दिखाई देते हैं। 1. दो ऑब्जेक्ट्स को बराबर माना जाएगा यदि * सभी * उनके फ़ील्ड मेल खाते हैं। यह हमेशा आप जो चाहते हैं वह नहीं हो सकता है। 2. एक वर्ग केवल एक अन्य वर्ग का विस्तार कर सकता है। क्या आप वास्तव में अन्य वर्गों को विरासत में लाने से अपने वर्गों को लॉक करना चाहते हैं क्योंकि 'बराबर' और 'हैशकोड' पुन: उपयोग करना अधिक महत्वपूर्ण है? – CKing

उत्तर

5

मैं इस दृष्टिकोण पहले से ही साथ दो मुद्दों देखें:

  1. दो वस्तुओं बराबर माना जाएगा यदि और केवल यदि उनके सभी क्षेत्रों से मेल खाते हैं। असली दुनिया में यह हमेशा मामला नहीं है।
  2. extendAbstractHashable से अपनी कक्षाएं बनाकर, अब आप extend किसी भी अन्य कक्षा से नहीं कर सकते हैं। यह equals और hashCode का पुन: उपयोग करने के लिए भुगतान करने के लिए काफी अधिक कीमत है।

पहला अंक आपके AbstractHashable कक्षा में अधिक मेटा डेटा पास करके हल किया जा सकता है जो यह पहचानने की अनुमति देता है कि कौन से फ़ील्ड वैकल्पिक हैं। उदाहरण के लिए, आप AbstractHashTable पर एक और सरणी पास कर सकते हैं जिसमें इंडेक्स स्थितियों को एक सेटटर के माध्यम से इसके तत्वों के रूप में अनदेखा किया जा सकता है। दूसरी समस्या संरचना का उपयोग कर हल की जा सकती है। AbstractHashTable को विस्तारित करने के बजाय, इसे HAS-AIS-A संबंधों के बजाय अपने उपयोगकर्ताओं के साथ संबंध स्थापित कर सकते हैं।

लेकिन, जैसा कि मैंने कभी समान (उत्तर ऊपर लिंक को छोड़कर) कुछ देखा है के लिए याद करते हैं नहीं है, मैं विशेष रूप से पूछने के लिए कि क्या वहाँ इस दृष्टिकोण

इस के खिलाफ कुछ बिंदु है चाहते हैं दृष्टिकोण निश्चित रूप से आपके कोड के पठनीयता पहलू को प्रभावित करेगा। यदि आप अधिक पढ़ने योग्य दृष्टिकोण (एनोटेशन का उपयोग करके कह सकते हैं) के साथ आ सकते हैं, तो मुझे equals और hashCode कार्यान्वयन का पुन: उपयोग करने के साथ कुछ भी गलत नहीं दिख रहा है।

यह सब कहा जा रहा है कि ग्रहण जैसे आधुनिक आईडीई आसानी से equals और hashCode कार्यान्वयन को आपके लिए कार्यान्वित कर सकते हैं, इसलिए वास्तव में इस तरह के समाधान के साथ आने की आवश्यकता है? मेरा मानना ​​है कि नहीं

+1

धन्यवाद, ये दो दिलचस्प अंक हैं (विशेष रूप से पहला, जिसे मैंने नहीं सोचा था)। एक्लिप्स द्वारा ऑटोजेनरेशन जब मैं प्रश्न के साथ आया था तो मैं इसका उपयोग कर रहा था: मुझे उस बॉयलरप्लेट कोड को पसंद नहीं आया, भले ही यह स्वत: उत्पन्न हो (भले ही फ़ील्ड जोड़ने जैसे परिवर्तन अधिक त्रुटि-प्रवण हो जाएं), लेकिन शायद मैं अभी इसके साथ सौदा करें। – rrobby86

0

मेरे अनुभव के आधार पर यह बुरा लगता है क्योंकि आप रनटाइम बनाम संकलन समय में प्राप्त त्रुटि को बढ़ाएंगे (याद रखें इन सूचियों के सभी उपयोग सूचियों आदि में अब आपको अनपेक्षित व्यवहार दे सकते हैं)। क्या होगा यदि कक्षाओं में खेतों का क्रम अलग है? दूसरा आप विरासत का दुरुपयोग कर रहे हैं? हो सकता है कि कुछ ढांचे (https://projectlombok.org/) का चयन करें जो एन्होकोड उत्पन्न करता है और एनोटेशन के आधार पर बराबर होता है?

+0

* यदि कक्षाओं में फ़ील्ड का क्रम अलग है तो क्या होगा *। उप-वर्गों में निर्माता (उदाहरण के लिए 'कुछ ऑब्जेक्ट') यह हमेशा एक निश्चित आदेश लागू नहीं करेगा जिसमें फ़ील्ड को इंटिलाइज किया जाएगा। इसके अलावा, आप एक ही कक्षा की वस्तुओं की तुलना कर रहे हैं तो फील्ड ऑर्डर कैसे बदल जाएगा? – CKing

+0

आप सही हैं, आप एक ही कक्षा की वस्तुओं की तुलना कर रहे हैं, लेकिन फिर आपके पास क्यों है (fields.length! = Other.fields.length)? – HoleInVoid

+0

ओपी ने चरम मामलों की जांच की है जहां डेवलपर्स ऑब्जेक्ट को तुरंत चालू करने के लिए एक ही निर्माता का उपयोग नहीं करते हैं। – CKing

0

समाधान काम करना चाहिए।

हालांकि यह देखने के OOP बिंदु से थोड़ा कमजोर महसूस करता है:

  • आप डेटा (सुपर क्लास वस्तु में क्षेत्र बनाम में क्षेत्रों) डुप्लिकेट कर रहे हैं, तो आपके वस्तु दो बार के रूप बड़ी है; भी अगर तुम कभी उन्हें परिवर्तनशील बनाने की जरूरत है, तो आप/hashCode

आधुनिक IDEs काफी आसानी से ऐसे तरीकों उत्पन्न करते हैं, अगर दो स्थानों पर राज्य बनाए रखने के लिए

  • वर्ग पदानुक्रम के लिए मजबूर किया जाता है, केवल बराबरी उपलब्ध कराने की आवश्यकता होगी आप चाहते हैं कि आप इन विधियों को सरल बनाने के लिए ढांचे (जैसे लंबोक) या पुस्तकालयों (जैसे गुवा के ऑब्जेक्ट्स/जावा 8 के ऑब्जेक्ट्स) का भी उपयोग कर सकें।

  • 0

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

    +0

    आपके सुझाव के लिए धन्यवाद। हालांकि, प्रतिबिंब इष्टतम प्रदर्शन नहीं करता है और मुझे लगता है कि यह किसी ऑब्जेक्ट के लिए निर्भर वस्तुओं के बड़े ग्राफ (जो मेरे मामले में हो सकता है) के लिए ध्यान देने योग्य हो सकता है। – rrobby86

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