Kotlin密封类-如何按密封类排序类似于按枚举排序

时间:2018-12-09 01:43:02

标签: sorting kotlin enums

在Java中,我们可以轻松地按枚举对集合进行排序,如下所示:

Collections.sort(toSortEnumList, new Comparator<theEnum>() {
                @Override
                public int compare(theEnum o1, theEnum o2) {
                    return o1.ordinal().compareTo(o2.ordinal());
                }
            });

和toSortEnumList将按升序排序。如何使用Kotlin密封课程来做到这一点?到目前为止,这是我尝试过的,但我尝试过按类名排序,但不是按枚举位置排序。必须有某种方式来按枚举位置排序:

    sealed class GoodCountries {

class Brazil : GoodCountries() {}

        class USA : GoodCountries() {}

        class Germany : GoodCountries() {}

        class China : GoodCountries() {}

    }


 //later on

 var toSortList = listOf<GoodCountries>(China(), Brazil(), USA(), Germany())

    Collections.sort(
        toSortList,
        { x: GoodCountries, y: GoodCountries -> y::class.java.name.compareTo(x::class.java.name) })

    Log.v("myTag", toSortList.toString())

此打印:

美国德国,中国,巴西-降序排列。不是我想要的。我想按密封类顺序排序(如枚举Java中的序数),我认为密封类应该比枚举更好,但是如果我做不到,也许枚举有优势。有人可以帮忙吗?

但我要打印:巴西,美国,德国,中国

更新:感谢Roland帮助我找到了密封类的列表。但现在我想按它排序:这是我到目前为止的内容:

Collections.sort(toSortList, object : Comparator<GoodCountries> {
            override fun compare(left: GoodCountries, right: GoodCountries): Int {
                return Integer.compare(GoodCountries::class.sealedSubclasses.indexOf(left), GoodCountries::class.sealedSubclasses.indexOf(right))
            }
        })

但出现以下错误:

enter image description here

3 个答案:

答案 0 :(得分:2)

您的问题的快速答案是在比较器中切换xy,即x::class.java.name.compareTo(y::class.java.name)

较长的答案是,对于您的用例,枚举可能更好。当某些子类看上去与其他子类不同时,密封类就会发光,拥有多个子类实例是有意义的。例如,具有子类sealed class ResultSuccess的{​​{1}},其中Error保存数据,而Success保存异常。假设您希望所有国家一视同仁,您的用例似乎更适合于传统枚举。

答案 1 :(得分:2)

您可以给GoodCountries一个order属性,并在子类中覆盖它,如下所示:

sealed class GoodCountries {
    abstract val order: Int
    class Brazil : GoodCountries() { override val order = 0 }
    class USA : GoodCountries() { override val order = 1 }
    class Germany : GoodCountries() { override val order = 2 }
    class China : GoodCountries() { override val order = 3 }
}

这不是一个完美的解决方案,因为您必须手工枚举,但是这样可以保证所需的顺序。

这样做可以大大简化您的比较代码:

val sorted = toSortList.sortedBy(GoodCountries::order)
println(sorted.map { it::class.simpleName })

输出:

  

[巴西,美国,德国,中国]

答案 2 :(得分:1)

也许不是您想要的东西,但也许是...

虽然密封类似乎没有序数,但是您已经注意到,class本身(即GoodCountries::class.sealedSubclasses)上有sealedSubclasses。同样,似乎sealedSubclasses的顺序是已定义的类之一,即此列表中的Brazil始终排在第一位,USA其次,依此类推。如果它们并非全部嵌套(例如,如果其中一些在外部,则它们会首先列出)。

但是:文档没有指出此顺序是故意选择的。在'Sealed classes' reference documentationsealedSubclasses (k)documentation中都没有。

关于按密封的类顺序对实体进行排序的问题,您可能需要使用以下内容:

val entityList = listOf(Germany(), China(), USA(), Brazil(), Germany())
entityList.sortedBy { // or use sortedWith(compareBy {
  GoodCountries::class.sealedSubclasses.indexOf(it::class)
}.forEach(::println) // or toList...

或类似的东西

GoodCountries::class.sealedSubclasses
    .asSequence()
    .flatMap { klazzInOrder ->
      entityList.asSequence().filter { it::class == klazzInOrder }
    }
    .forEach(::println)

在性能方面,两者都不是最好的选择,但我想您会明白的。

我之前添加的排序样本(当我没有意识到实际上要对实体而不是类型进行排序时):

println("Listing the sealed classes in the order of their declaration*")
GoodCountries::class.sealedSubclasses.forEach(::println)

println("Listing the sealed classes ordered by their simple name")
GoodCountries::class.sealedSubclasses.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.simpleName!! })
  .forEach(::println)
// same result, but written differently
GoodCountries::class.sealedSubclasses.sortedBy { it.simpleName?.toLowerCase() }
  .forEach(::println)

您甚至可能希望结合使用nullsLastCASE_INSENSITIVE_ORDER(很可能是在不使用密封类的情况下),在这种情况下,您可能会编写如下内容:

GoodCountries::class.sealedSubclasses.sortedWith(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it.simpleName })
  .forEach(::println)