Kotlin:为了Comparable而摆脱Unchecked演员阵容

时间:2017-08-27 18:57:06

标签: generics kotlin

我刚刚实施了选择排序,以熟悉Kotlin并碰到一个未经检查的演员,我发现很难摆脱。实现基于kotlin.Comparable接口:

object SelectionSort {

    fun <T> sort(a: Array<Comparable<T>>) {
        val N = a.size
        for(i in 0..N - 2) {
            var min = i
            for(j in i + 1..N - 1) {
                if(less(a[j], a[min])) {
                    min = j
                }
            }
            exchange(a, i, min)
        }
    }

    private fun <T> exchange(a: Array<Comparable<T>>, i: Int, min: Int) {
        val temp = a[i]
        a[i] = a[min]
        a[min] = temp
    }

    private fun <T> less(comparable: Comparable<T>, comparable1: Comparable<T>): Boolean {
        return comparable < (comparable1 as T) // Here is the unchecked cast
    }
}

确切的消息是&#34; 未选中广告:Comparable<T>T &#34;。

我的偏好是使用kotlin.Comparable接口作为sort方法的主要参数以及辅助方法。但是如何以干净的方式比较一个与另一个相似的仿制药呢? 如果这是不可能的,那么使用接口实现诸如选择排序之类的东西会有什么干净的选择呢?

基于此answer,我重新编写了我的选择排序实现:

object SelectionSort {

    fun <T : Comparable<T>> sort(a: Array<T>) {
        val N = a.size
        for (i in 0..N - 2) {
            var min = i
            for (j in i + 1..N - 1) {
                if (less(a[j], a[min])) {
                    min = j
                }
            }
            exchange(a, i, min)
        }
    }

    fun <T> exchange(a: Array<T>, i: Int, min: Int) {
        val temp = a[i]
        a[i] = a[min]
        a[min] = temp
    }

    fun <T : Comparable<T>> less(c1: T, c2: T) =
            c1 < c2
}

此实施现在没有警告。

2 个答案:

答案 0 :(得分:2)

您应该在<T : Comparable<T>>sort上使用绑定less

fun <T : Comparable<T>> sort(arr: Array<T>) = ...
fun <T : Comparable<T>> less(l: T, r: T) = ...

如果没有合理的类型,没有理由采用具体的类型。

此外,sort 的参数不应为 Array<Comparable<T>>。 Kotlin中的数组不变:如果A extends B,则Array<A>不是Array<B>的子类型。这与Java不同,其中数组是协变的,String[]Object[]的子类型。不变性是正确的方法。如果您现在定义排序,则无法编写

val arr: Array<String> = ...
sort(arr);

因为sort需要Array<Comparable<String>>,但arrArray<String>

此外,没有理由在exchange

上加上额外的限制
fun <T> exchange(arr: Array<T>, i: Int, j: Int) = ...

答案 1 :(得分:1)

解决您的问题,但过于复杂

这解决了您的问题:

inline fun <reified T> less(c1: Comparable<T>, c2: Comparable<T>) =
   if (c2 is T) {
       c1 < c2
   } else {
       throw IllegalStateException() // whatever
   }

如果您首先检查其类型T,则无需强制转换。如果T成为reified,则可以这样做,即您可以在运行时访问它(默认情况下,泛型类型在运行时不可用)。只有当您methodinline时,才能再次执行此操作。 请注意,在类型检查之后访问c2可以而无需显式转换,因为编译器已经知道您正在做安全的事情,这称为"Smart Cast"

如您所见,此实现在错误处理方面并非100%正确 - 但这取决于您; - )

但是:不要这样做。

更好的解决方案

我建议您在实现中摆脱Comparable处理,以便sort方法看起来像这样:

 fun <T: Comparable<T>> sort(a: Array<T>)

这确保只能对实现T的{​​{1}}进行排序,Comparable在泛型类型中用:(上限)表示。然后您的less方法如下:

private fun <T : Comparable<T>> less(comparable: T, comparable1: T): Boolean {
    return comparable < comparable1
}

无需演员: - )

(同样适用于exchange