2010-02-08 15 views
12

मैं एक विशेषता लिखने की कोशिश कर रहा हूं (स्कैला 2.8 में) जिसे एक केस क्लास में मिश्रित किया जा सकता है, जिससे किसी विशेष डीबगिंग उद्देश्य के लिए रनटाइम पर अपने क्षेत्रों का निरीक्षण किया जा सकता है। मैं उन्हें क्रम में वापस प्राप्त करना चाहता हूं कि उन्हें स्रोत फ़ाइल में घोषित किया गया था, और मैं केस क्लास के अंदर किसी भी अन्य फ़ील्ड को छोड़ना चाहता हूं। उदाहरण के लिए:स्कैला केस क्लास पर प्रतिबिंब

trait CaseClassReflector extends Product { 

    def getFields: List[(String, Any)] = { 
    var fieldValueToName: Map[Any, String] = Map() 
    for (field <- getClass.getDeclaredFields) { 
     field.setAccessible(true) 
     fieldValueToName += (field.get(this) -> field.getName) 
    } 
    productIterator.toList map { value => fieldValueToName(value) -> value } 
    } 

} 

case class Colour(red: Int, green: Int, blue: Int) extends CaseClassReflector { 
    val other: Int = 42 
} 

scala> val c = Colour(234, 123, 23) 
c: Colour = Colour(234,123,23) 

scala> val fields = c.getFields  
fields: List[(String, Any)] = List((red,234), (green,123), (blue,23)) 

उपरोक्त क्रियान्वयन स्पष्ट रूप से त्रुटिपूर्ण है क्योंकि यह उत्पाद में एक क्षेत्र की स्थिति और उन मैदान पर मूल्य की समानता से इसके नाम के बीच संबंध का अनुमान लगाता है, ताकि निम्नलिखित, कहते हैं, काम नहीं करेगा :

Colour(0, 0, 0).getFields 

क्या इसका कोई तरीका लागू किया जा सकता है?

+0

वहाँ अपने कोड में एक बग है उदाहरण के आधार पर एक छोटी और काम संस्करण है,। मान अद्वितीय नहीं हैं, और इसलिए आप (फ़ील्ड.get (यह) -> field.getName) के साथ मानों को ओवरराइट करने जा रहे हैं, जब एक फ़ील्ड से आगे बढ़ते हैं तो नाम दिया जाता है। नीचे दिए गए कोड का एक पुनः लिखित संस्करण देखें। –

+0

@ SagieDavidovich वास्तव में, जैसा कि उल्लेख किया गया है, "उपर्युक्त कार्यान्वयन स्पष्ट रूप से त्रुटिपूर्ण है" –

उत्तर

7

प्रत्येक उदाहरण में मैंने देखा है कि फ़ील्ड रिवर्स ऑर्डर में हैं: GetFields सरणी में अंतिम आइटम केस क्लास में सूचीबद्ध पहला है। यदि आप केस क्लास "अच्छी तरह से" का उपयोग करते हैं, तो आपको productElement(n) को getDeclaredFields()(getDeclaredFields.length-n-1) पर मैप करने में सक्षम होना चाहिए।

लेकिन यह खतरनाक है, क्योंकि मुझे कल्पना में कुछ भी नहीं पता है कि यह इस तरह से होना चाहिए, और यदि आप केस क्लास में एक वैल ओवरराइड करते हैं, तो यह भी प्राप्त नहीं होगा फ़ील्ड फ़ील्ड (यह उस सुपरक्लास के क्षेत्र में दिखाई देगा)।

आप यह मानने के लिए अपना कोड बदल सकते हैं कि चीजें इस तरह से हैं, लेकिन जांचें कि उस नाम और उत्पाद के साथ गेटर विधि एक ही मूल्य लौटाती है और यदि वे नहीं करते हैं तो अपवाद फेंक दें (जिसका अर्थ है कि आप वास्तव में नहीं हैं पता है कि क्या से मेल खाता है)।

+1

मैट आर का सामना करने के समान समस्या का सामना कर रहा हूं। स्कैला में रिश्तेदार नोब होने के नाते, क्या आप कृपया अपने उत्तर को थोड़ा और समझा सकते हैं। यह काफी उपयोगी होगा। धन्यवाद! –

+2

@Core_Dumped - दरअसल, मुझे लगता है कि आपकी समस्या के समाधान के साथ आने में आपको परेशानी होने की अधिक संभावना है जब तक कि आप अपने स्वयं के समाधान तैयार करने के लिए उपरोक्त अपने अस्पष्ट संकेतों का उपयोग नहीं कर सकते। जैसा कि मैंने कहा, "यह बल्कि खतरनाक है"।समस्याओं की उम्मीद और उनसे बचने में सक्षम होना आपकी ज़िम्मेदारी है, जिसके लिए कम से कम इस पहलू में "रिश्तेदार नोब" से "नहीं" में संक्रमण की आवश्यकता हो सकती है। आरईपीएल में प्रतिबिंब और नमूना केस कक्षाओं के साथ खेलें और देखें कि क्या आप इसे समझ सकते हैं! –

+0

getClass.getDeclaredFields.map (_। GetName) .zip (productIterator.toList) .toMap –

10

ट्रंक में देखें और आपको यह मिल जाएगा। टिप्पणी करने के लिए सुनो, इस समर्थित नहीं है: लेकिन जब से मैं भी उन नामों की जरूरत ...

/** private[scala] so nobody gets the idea this is a supported interface. 
*/ 
private[scala] def caseParamNames(path: String): Option[List[String]] = { 
    val (outer, inner) = (path indexOf '$') match { 
    case -1 => (path, "") 
    case x => (path take x, path drop (x + 1)) 
    } 

    for { 
    clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer) 
    ssig <- ScalaSigParser.parse(clazz) 
    } 
    yield { 
    val f: PartialFunction[Symbol, List[String]] = 
     if (inner.isEmpty) { 
     case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1) 
     } 
     else { 
     case x: ClassSymbol if x.name == inner => 
      val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " ")) 
      xs.toList map (_.name dropRight 1) 
     } 

    (ssig.symbols partialMap f).flatten toList 
    } 
} 
4

तुम भी ProductCompletion दुभाषिया पैकेज से मामले वर्गों के नाम और गुणधर्म मान को पाने के लिए उपयोग कर सकते हैं:

import tools.nsc.interpreter.ProductCompletion 

// get attribute names 
new ProductCompletion(Colour(1, 2, 3)).caseNames 
// returns: List(red, green, blue) 

// get attribute values 
new ProductCompletion(Colour(1, 2, 3)).caseFields 

संपादित करें: रॉलेंड द्वारा संकेत और virtualeyes

यह आवश्यक हैशामिल करने के लिएलाइब्रेरी जो scala-lang collection का हिस्सा है।

आपके संकेत, रोलैंड और virtualeyes के लिए धन्यवाद।

+1

ध्यान दें कि 'केस नाम' पर कॉल केवल तभी काम करता है जब स्केलप (http://www.scala-lang.org/node/292) कक्षापथ पर पाया जा सकता है। अन्यथा एक खाली सूची वापस कर दी जाएगी (स्कैला 2.9.1 का उपयोग करते समय)। –

+1

+1 @roland, यह सच है कि आप क्या कहते हैं। यह देखते हुए कि स्केलैप डाउनलोड Google खोज में बिल्कुल छलांग नहीं लगाता है, यहां यह 2.9.1 है: https://oss.sonatype.org/content/groups/scala-tools/org/scala-lang/scalap/2.9। 1/ – virtualeyes

+0

उस पर प्रतिबिंबित करने में सक्षम होने से पहले केस क्लास इंस्टेंस की आवश्यकता के कैच 22 को नोट करें। उम्मीद है कि 2.10 इस संबंध में बात करने के लिए सामान लाएंगे, हाथ 2.9 में बंधे हैं, ब्लैक बॉक्स केस क्लास के साथ काम करना एक पिटा है, डोमेन मॉडल टाइपिंग, ओआरएम मैपिंग, और ट्रिपलिकेट, वाईटीएफ में सत्यापन ... – virtualeyes

9

यहाँ ऊपर

trait CaseClassReflector extends Product { 
    def getFields = getClass.getDeclaredFields.map(field => { 
     field setAccessible true 
     field.getName -> field.get(this) 
    }) 
    } 
+0

I कोशिश की और यह कम से कम एक साधारण मामले वर्ग के लिए काम किया। –