【问题标题】:Kotlin sealed class - how to sort by sealed class similar to sorting by enumKotlin 密封类 - 如何按密封类排序类似于按枚举排序
【发布时间】:2018-12-09 01:43:01
【问题描述】:

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

但我在indexOf 收到以下错误:

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

【问题讨论】:

    标签: sorting kotlin enums


    【解决方案1】:

    您可以像这样为您的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 属性,而不是使其成为被子类)。

    【讨论】:

    • 或者,如果您不喜欢覆盖,您可以随时执行以下操作:gist.github.com/chriscoomber/c351a2075eeff005f1e6bc4c8143b4af(我个人的偏好是避免 OOP,因为它不会增加任何明显的价值,因为它通常是更令人困惑。在我的链接中,我们只是像使用密封特性一样使用密封类。)
    • @Chrispher 我只能同意。我更新了我的答案:)
    【解决方案2】:

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

    虽然密封类似乎没有您已经注意到的类似序数的东西,但 class 本身有 sealedSubclasses(即 GoodCountries::class.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)
    

    【讨论】:

    • 你能告诉我如何进行排序吗?感谢您的帮助,我发现了以下内容:GoodCountries::class.nestedClasses 但它以错误的顺序给了我课程
    • 您使用哪个版本的 Kotlin?由于 1.3 存在 sealedSubclasses,您应该尝试而不是 nestedClasses.. 示例实际上在答案中:GoodCountries::class.sealedSubclasses
    • 你可能需要kotlin-reflect-dependency...sealedSubclassesWhat's New in Kotlin 1.3中也提到过
    • 你现在能帮我整理一下吗?我有以下内容,但它说它无法推断类型,并且 indexOf 用红色下划线: Collections.sort(toSortList, object : Comparator { override fun compare(left: GoodCountries, right: GoodCountries): Int { return Integer. compare(GoodCountries::class.sealedSubclasses.indexOf(left), GoodCountries::class.sealedSubclasses.indexOf(right)) } }) 我更新了我的问题,所以你可以看到我到目前为止做了什么
    • @j2emanue 为什么要对GoodCountries 密封的子类进行排序?如果您访问sealedSubclasses,则订单是他们的声明之一* ...但正如答案中所述,不知道这是否是故意的...所以请谨慎对待...我不理解您的示例代码...如果您只是想以〜基于字符串的顺序对其进行排序,您可能只想这样做:GoodCountries::class.sealedSubclasses.sortedBy { it.simpleName }...但我理解您的方式,您想要那里的顺序声明......答案基本上描述了;-)
    【解决方案3】:

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

    更长的答案是,对于您的用例,枚举可能会更好。当某些子类看起来与其他子类不同时,密封类会发光,并且拥有它们的多个实例是有意义的。例如,sealed class Result 具有子类 SuccessError,其中 Success 保存数据,Error 保存异常。假设您想对所有国家/地区一视同仁,您的用例似乎更适合传统枚举。

    【讨论】:

    • 我想按枚举的 ORDER 而不是我的升序对列表进行排序。您建议的比较器如何做到这一点?它正在做字典排序。我想要枚举对不起。让我知道密封类是否不可能
    • 哦,我明白了。我不确定有什么方法可以在运行时获得该订单。如果需要,请使用枚举。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    相关资源
    最近更新 更多