我试图写一个比较两个可比数据列表的函数。可比较的数据可以是各种类型,只要被比较的两个列表中相同位置的元素是可比较的。例如:
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,因为
因此list1&lt; list2中。
以下是我提供的代码以及一些试错过程,因为我对这个特定情况下泛型如何工作有点困惑:
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,&#34; abc&#34;]时,我得到了
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在这个迭代中意味着是整数,那么为什么我不能对它进行类型检查呢?
答案 0 :(得分:14)
Comparable
是类型参数T
的接口逆变。类型T
的值仅允许在in
- 位置,即作为类方法的参数而不是返回值。
interface Comparable<in T> {
abstract operator fun compareTo(other: T): Int
}
逆变型的星型投影等同于使用Nothing
参数化的类型,因此Comparable<*>
实际上是Comparable<in Nothing>
。这意味着一旦你有一个未知类型的可比较,你就无法安全地将它与除了Nothing
类型的值之外的任何东西进行比较,这已知没有值。 :)
如果您尝试将Int
与String
进行比较,则可能会遇到此类不安全的后果。这不是elem2 as T
抛出ClassCastException(它实际上是一个未经检查的强制转换,因为你已经压制状态的警告),它是String.compareTo
的实现,当它遇到的东西不是{{1 }}
回到问题,您可以借助库函数kotlin.comparisons.compareValues实现此类列表比较。它知道如何处理空值并隐藏令人讨厌的未经检查的强制转换。
String
注意,由于泛型中的类型擦除确保值具有相同的类(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>
。