2016-02-19 4 views
7

に比較対象のリストを比較します。比較対象の2つのリストの同じ位置にある要素が同等である限り、比較対象はさまざまな種類のものがあります。例:は、私が比較対象の二つのリストを比較する関数を記述しようとしているKotlin

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

  • "XYZ" == "XYZ"
  • '' < 'B'
    • 10ので、

      と< list2のため、リスト1。ここで

      は、私はジェネリックは、この特定のケースでどのように機能するかについて少し困惑しているので、私は試行錯誤のビットと一緒に入れているコードです:

      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) 
      

      で型キャストで発生しますTはここで整数に解決されましたか?実際に、私はこれが実際にコンパイルされるのに驚いています。

      そして第三に、未チェックのキャストを取り除くための方法は何ですか?試しました

      if (elem2 !is T) 
          // throw Exception 
      

      しかし、それはコンパイルされませんでした。どうして?何とかTがこの繰り返しでIntegerであることが分かっているようですが、どうして私はそれに対して型チェックできませんか?

    答えて

    9

    Comparableは、そのタイプパラメータTに対しての反例のインタフェースです。タイプTの値は、inの位置、つまりクラスメソッドのパラメータとしてのみで、戻り値としては許可されていません。

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

    反変タイプの星型投影は、このようComparable<*>が実際Comparable<in Nothing>ある、Nothingでパラメータ化、その型と等価です。これは、一度あなたが不明な型のComparableを持っていれば、それが無価値であることが知られているNothing型の値を除いて安全にそれを比較することができないことを意味します。あなたはStringIntを比較しようとすると:)

    あなたは、このような危難の結果に遭遇することができます。 elem2 as TがClassCastExceptionをスローしたわけではありません(実際には、状態を抑制しているという警告としてチェックされていないキャストです)。の実装は、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

    詳しい説明とより洗練されたソリューションについては、ありがとうございます。非常に役立ちます! –