2013-10-04 5 views
12

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

पृष्ठभूमि: मेरे पास एक ऐसा केस क्लास है जो समानता को ओवरराइड करता है और इस प्रकार संरचनात्मक बराबर उत्पन्न नहीं करता है (स्क्वायरल कीडइन्टिटी, संबंधित: Prevent Mixin overriding equals from breaking case class equality), और मैं अभी इसे बदलने की स्थिति में नहीं हूं । यह एक यूनिट परीक्षण के लिए है, इसलिए दोनों उदाहरण जारी नहीं हैं।

+2

केस कक्षाओं की वस्तुओं में आप 'productIterator.toList' का उपयोग कर सकते हैं यदि आप केवल श्रेणी मूल्यों में रुचि रखते हैं। – pedrofurla

+0

productIterator.toList काम नहीं किया। यह सिर्फ एक सूची में उदाहरण डाल दिया। – ttt

+0

दोनों ऑब्जेक्ट्स को प्रतिबिंबित करें और अपने फ़ील्ड और मानों की तुलना करें। – Ashalynd

उत्तर

1

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

implicit class ReallyEqual[T](a: T) { 
    import java.lang.reflect.Modifier._ 
    def ==:==[U](b: U): Boolean = { 
    val fieldsA = a.getClass.getDeclaredFields 
    val methodsA = a.getClass.getDeclaredMethods 
    val mapA = (methodsA.toList map (z => (z.getName(), z))).toMap 
    val fieldsB = b.getClass.getDeclaredFields 
    val methodsB = b.getClass.getDeclaredMethods 
    val mapB = (methodsB.toList map (z => (z.getName(), z))).toMap 
    fieldsA.length == fieldsB.length && 
    (true /: (fieldsA zip fieldsB)){ case (res, (aa, bb)) => 
     if(((aa.getModifiers & STATIC) != 0) || ((bb.getModifiers & STATIC) != 0)) { res // ignore 
     } else if(((aa.getModifiers & (PRIVATE | PROTECTED)) == 0) && ((bb.getModifiers & (PRIVATE | PROTECTED)) == 0)) { 
     res && aa == bb && aa.get(a) ==:== bb.get(b) 
     } else if((mapA contains aa.getName) && (mapB contains bb.getName)) { 
     res && mapA(aa.getName).invoke(a) == mapB(aa.getName).invoke(b) 
     } else res 
    } 
    } 
} 

trait EqualBad { 
    override def equals(other: Any) = false 
} 

case class MyCaseClass(x: Int, y: List[Int]) extends EqualBad 

MyCaseClass(1, List(1, 2)) ==:== MyCaseClass(1, List(1, 2)) // true 
MyCaseClass(1, List(1, 2)) ==:== MyCaseClass(1, List(2, 2)) // false 
MyCaseClass(1, List(1, 2)) ==:== MyCaseClass(2, List(1, 2)) // false 
MyCaseClass(1, List(1, 2)) == MyCaseClass(1, List(1, 2)) // false 

तुम भी विधि में पिछले == बदलकर इसे और अधिक पुनरावर्ती कर सकते हैं ReallyEqual को ==: ==

सावधान: यह पूर्णांकों के लिए काम नहीं करेगा, क्योंकि इंट नहीं है कोई क्षेत्र या विधियां। पूर्व: 1 ==: == 2 सत्य वापस आ जाएगा।

1

AFAIK स्कैला मानक पुस्तकालय में ऐसा कोई फ़ंक्शन नहीं है। लेकिन आपके पास दो विकल्प हैं।

  1. आप किसी मैक्रो है, जो दो दिए गए मान
  2. उपयोग जावा प्रतिबिंब की समानता तुलना करने के लिए करता है, तो प्रदर्शन कोई फर्क नहीं पड़ता एक विधि उत्पन्न लिखें। यह उत्तर देखें: Is there a Java reflection utility to do a deep comparison of two objects?
+1

यदि आप ऐसा मैक्रो लिखते हैं, तो यह उत्तर एक बक्षीस जीत सकता है। –

+4

3. स्कैला प्रतिबिंब का उपयोग करें। –