2016-02-19 32 views
7

में तुलनात्मक की सूचियों की तुलना करना मैं एक ऐसा फ़ंक्शन लिखने की कोशिश कर रहा हूं जो तुलनात्मक की दो सूचियों की तुलना करता है। तुलनात्मक रूप से विभिन्न प्रकार के हो सकते हैं जब तक कि दो सूचियों की तुलना में समान पदों के तत्व तुलनीय हैं। उदाहरण:कोटलिन

val list1 = ArrayList<Comparable<*>>() 
    val list2 = ArrayList<Comparable<*>>() 

    list1.add(10) 
    list1.add("xyz") 
    list1.add('a') 

    list2.add(10) 
    list2.add("xyz") 
    list2.add('b') 

    println(compare(list1, list2)) 

यह प्रिंट चाहिए -1 क्योंकि

  • 10 == 10
  • "xyz" == "xyz"
  • 'एक' < 'बी'

और इस प्रकार सूची 1 < सूची 2।

यहाँ कोड है कि मैं एक परीक्षण और त्रुटि प्रक्रिया का एक सा के साथ एक साथ डाल दिया है के बाद से मैं के बारे में कैसे जेनरिक इस विशिष्ट मामले में काम उलझन में एक छोटा सा हूँ:

fun <T> compare(list1: List<Comparable<T>>, list2: List<Comparable<T>>): Int { 
    for (i in 0..Math.max(list1.size, list2.size) - 1) { 
     val elem1 = if (i < list1.size) list1[i] else null 
     val elem2 = if (i < list2.size) list2[i] else null 

     if (elem1 == null && elem2 == null) 
      return 0 

     if (elem1 == null) 
      return -1 

     if (elem2 == null) 
      return 1 

     @Suppress("UNCHECKED_CAST") 
     val comparisonResult = elem1.compareTo(elem2 as T) 

     if (comparisonResult != 0) 
      return comparisonResult 
    } 

    return 0 
} 

और यह वास्तव में संकलित करता है तथा उम्मीद के अनुसार काम करता है, लेकिन कुछ चीजें हैं जिनके बारे में मैं परेशान हूं।

मेरा पहला प्रयास निम्नलिखित विधि हस्ताक्षर के साथ था:

fun compare(list1: List<Comparable<*>>, list2: List<Comparable<*>>): Int 

यह हालांकि संकलन नहीं किया। ऐसा क्यों है? और यह घोषणा दूसरे से अलग कैसे है?

दूसरा, यदि मैं मिलान की स्थिति में अतुलनीय मानों के साथ सूचियों की तुलना करने का प्रयास करता हूं, तो मुझे एक प्रकार का कास्ट त्रुटि मिलती है। उदाहरण के लिए, जब की तुलना [1,1] के लिए [1, "abc"], मैं

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 

जाहिरा तौर पर

elem1.compareTo(elem2 as T) 

में टाइप कास्ट पर उठता मिल मुझे क्या पहेली: कैसे है टी यहाँ पूर्णांक करने के लिए हल किया? वास्तव में, मुझे हैरान है कि यह वास्तव में संकलित करता है।

और तीसरा, अनचेक कास्ट से छुटकारा पाने का कोई तरीका है? मैंने कोशिश की

if (elem2 !is T) 
    // throw Exception 

लेकिन यह संकलित नहीं हुआ। क्यूं कर? ऐसा लगता है कि किसी भी तरह से यह ज्ञात है कि टी इस पुनरावृत्ति में पूर्णांक होने के लिए है, तो मैं इसके खिलाफ टाइप-चेक क्यों नहीं कर सकता?

उत्तर

9

Comparable एक इंटरफेस contravariant इसके प्रकार पैरामीटर T पर है। T के मानों को केवल in -positions पर अनुमत किया जाता है, अर्थात् कक्षा विधियों के पैरामीटर के रूप में और वापसी मान के रूप में नहीं।

interface Comparable<in T> { 
    abstract operator fun compareTo(other: T): Int 
} 

contravariant प्रकार का एक स्टार के प्रक्षेपण उस प्रकार Nothing साथ parametrized के बराबर है, इस प्रकार Comparable<*> वास्तव में एक Comparable<in Nothing> है। इसका अर्थ यह है कि एक बार जब आपके पास अज्ञात प्रकार का एक तुलनात्मक हो, तो आप Nothing प्रकार के मान को छोड़कर इसे किसी भी चीज़ से सुरक्षित रूप से तुलना नहीं कर सकते, जिसे कोई मान नहीं है।:)

आप इस तरह के unsafety के परिणाम का सामना करता है, तो आप एक String के साथ एक Int तुलना करने की कोशिश कर सकते हैं। यह elem2 as T क्लासकास्टएक्सप्शन नहीं फेंकता है (यह वास्तव में एक अनचेक कलाकार है जिसे आपने दबाने वाले राज्यों के रूप में चेतावनी दी है), यह String.compareTo का कार्यान्वयन है जो फेंकता है, जब यह String नहीं मिलता है।

प्रश्न पर वापस लौटने पर, आप लाइब्रेरी फ़ंक्शन kotlin.comparisons.compareValues की सहायता से ऐसी सूची तुलना लागू कर सकते हैं। यह जानता है कि नल को कैसे संभालना है और अंदर गंदे अनचेक कास्ट छुपाएं।

import kotlin.comparisons.* 

fun compareLists(list1: List<Comparable<*>>, list2: List<Comparable<*>>): Int { 
    for (i in 0..Math.min(list1.size, list2.size)-1) { 
     val elem1 = list1[i] 
     val elem2 = list2[i] 

     if (elem1.javaClass != elem2.javaClass) { 
      TODO("Decide what to do when you encounter values of different classes") 
     } 

     compareValues(elem1, elem2).let { 
      if (it != 0) return it 
     } 
    } 
    return compareValues(list1.size, list2.size) 
} 

ध्यान दें, मानों यह सुनिश्चित जेनरिक में टाइप विलोपन की वजह से एक ही कक्षा (elem1.javaClass == elem2.javaClass) मतलब यह नहीं है आवश्यक मूल्यों को सुरक्षित रूप से तुलना की जा सकता है। उदाहरण के लिए List<Int> और List<String> दोनों में एक ही कक्षा List है।

+0

विस्तृत स्पष्टीकरण और एक अधिक सुरुचिपूर्ण समाधान के लिए, इल्या, बहुत बहुत धन्यवाद। बहुत उपयोगी! –