簡體   English   中英

Kotlin 密封類 - 如何按類似於按枚舉排序的密封類排序

[英]Kotlin sealed class - how to sort by sealed class similar to sorting by enum

在 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 枚舉中的序數)排序,如下所示:

巴西、美國、德國、中國

我認為密封類應該比枚舉更好,但如果我不能這樣做,也許枚舉有優勢。

更新:多虧了羅蘭的幫助,我才能找到密封類的列表。 但現在我想按它排序:這是我目前所擁有的:

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)
        )
    }
})

但我在indexOf收到以下錯誤:

類型推斷失敗。 類型參數 T 的值應在輸入類型(參數類型、接收器類型或預期類型)中提及。 嘗試明確指定它。

你可以給你的GoodCountries一個order屬性,如下所示:

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

這不是一個完美的解決方案,因為您必須手動枚舉,但這樣可以保證所需的順序。

這樣做將大大簡化您的比較代碼:

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

輸出:

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

編輯:更新了Chrispher 的好主意,這使代碼更加清晰(在GoodCountries構造函數中具有order屬性,而不是使其成為被子類覆蓋的抽象變量)。

您的問題的快速答案是在比較器中切換xy ,即x::class.java.name.compareTo(y::class.java.name)

更長的答案是,對於您的用例,枚舉可能更好。 當某些子類看起來與其他子類不同時,密封類會發光,並且擁有它們的多個實例很有意義。 例如,帶有子類SuccessErrorsealed class Result ,其中Success保存數據, Error保存異常。 假設您希望對所有國家/地區一視同仁,您的用例似乎更適合傳統枚舉。

也許不完全是你正在尋找的,但也許它是......

雖然密封類似乎沒有像序數這樣的東西,你已經注意到了,但class本身有sealedSubclasses (即GoodCountries::class.sealedSubclasses )。 此外,似乎sealedSubclasses的順序是定義的類之一,即此列表中的Brazil始終排在第一位, USA第二位,等等。如果它們不是全部嵌套(即,如果有些在外面,它們首先列出)。

但是:文檔並沒有說明這個順序是故意選擇的。 無論是在“密封類”參考文檔中,還是在sealedSubclasses (k) 文檔中

關於在密封類順序中對實體進行排序的問題,您可能需要使用以下內容:

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM