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

j2e*_*nue 7 sorting enums kotlin

在 Java 中,我们可以轻松地通过枚举对 Collection 进行排序,如下所示:

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

并且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())
Run Code Online (Sandbox Code Playgroud)

这打印:

美国、德国、中国、巴西

降序。不是我想要的。我想按密封类顺序(如 Java 枚举中的序数)排序,如下所示:

巴西、美国、德国、中国

我认为密封类应该比枚举更好,但如果我做不到这一点,也许枚举有优势。

更新:感谢罗兰的帮助,我找到了密封班级的列表。但现在我想按它排序:这是我到目前为止所拥有的:

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)
        )
    }
})
Run Code Online (Sandbox Code Playgroud)

但我在以下位置收到以下错误indexOf

类型推断失败。类型参数 T 的值应该在输入类型(参数类型、接收者类型或预期类型)中提及。尝试明确指定它。

Wil*_*zel 9

你可以给你GoodCountries一个order像这样的财产:

sealed class GoodCountries(val order: Int) {
    class Brazil : GoodCountries(0)
    class USA : GoodCountries(1)
    class Germany : GoodCountries(2)
    class China : GoodCountries(3)
}
Run Code Online (Sandbox Code Playgroud)

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

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

val sorted = toSortList.sortedBy(GoodCountries::order)
println(sorted.map { it::class.simpleName })
Run Code Online (Sandbox Code Playgroud)

输出:

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

编辑:用Chrispher的好主意进行更新,使代码更加清晰(order在构造函数中具有属性GoodCountries,而不是使其成为由子类覆盖的抽象变量)。


Rol*_*and 2

也许不完全是您正在寻找的,但也许它是......

虽然密封类似乎没有像序数之类的东西,您已经注意到了,但它本身是sealedSubclasses有的class(即GoodCountries::class.sealedSubclasses)。另外,似乎 的顺序sealedSubclasses是定义的类之一,即Brazil在此列表中总是第一,USA第二等。如果它们不是全部嵌套,则顺序会有所不同(即,如果有些在外部,则它们是首先列出)。

然而:文档并没有说明这个顺序是故意选择的。“密封类”参考文档sealedSubclasses(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...
Run Code Online (Sandbox Code Playgroud)

或类似的东西:

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

就性能而言,两者可能都不是最佳选择,但我想您已经明白了。

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

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)
Run Code Online (Sandbox Code Playgroud)

您甚至可能想要组合nullsLastand CASE_INSENSITIVE_ORDER(最有可能的是,如果您不处理密封类),在这种情况下您会编写如下内容:

GoodCountries::class.sealedSubclasses.sortedWith(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it.simpleName })
  .forEach(::println)
Run Code Online (Sandbox Code Playgroud)