代码之家  ›  专栏  ›  技术社区  ›  j2emanue

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

  •  2
  • j2emanue  · 技术社区  · 7 年前

    在Java中,我们可以用EnUM轻松地排序这样一个集合:

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

    ToSortEnumList将按升序排列。我怎样才能在一个封闭的课堂上做到这一点?这是迄今为止我尝试的方法,我尝试按类名排序,但不是按枚举位置排序。必须有某种方法按枚举位置排序:

        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中的序号),我认为密封类应该比枚举好,但是如果我不能这样做,Enness有一个优势。有人能帮忙吗?

    但我想把它印出来:巴西,美国,德国,中国

    更新 :多亏了罗兰的帮助,我可以找到密封类的列表。但现在我想按它排序:这是迄今为止我所拥有的:

    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 回复  |  直到 7 年前
        1
  •  1
  •   Roland    7 年前

    也许不是你想要的,但也许是…

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

    但是:文档并没有说明这是故意选择的。都不在 'Sealed classes' reference documentation 也不在 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...
    

    或者类似的:

    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)
    

    你甚至可能想结合 nullsLast CASE_INSENSITIVE_ORDER (最可能的情况是,如果您不处理密封类),在这种情况下,您将编写如下内容:

    GoodCountries::class.sealedSubclasses.sortedWith(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it.simpleName })
      .forEach(::println)
    
        2
  •  2
  •   RussHWolf    7 年前

    你问题的快速答案是转换 x y 在你的比较器里 x::class.java.name.compareTo(y::class.java.name)

    更长的答案是,对于您的用例,枚举可能更好。当某些子类看起来与其他子类不同时,密封类就会发光,并且拥有它们的多个实例是有意义的。例如A sealed class Result 带子类 Success Error 在哪里 成功 保存数据和 误差 持有例外。假设您希望对所有国家/地区都进行相同的处理,那么您的用例似乎更适合于传统的枚举。

        3
  •  2
  •   Willi Mentzel user670265    7 年前

    你可以给你 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 })
    

    输出:

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