Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin sealed class - how to sort by sealed class similar to sorting by enum

In Java we can easily sort a Collecion by enum with something like this:

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

and the toSortEnumList will be ordered ascending. How can I do this with a Kotlin's sealed classes? I tried sorting by the class names but that's not by enum position. There must be some way to sort by enum position:

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

this prints:

USA, Germany, China, Brazil

descending order. Not what I want. I want to sort by sealed class order (like ordinal number in Java's enum) like this:

Brazil, USA, Germany, China

I thought sealed classes are supposed to be better then enums but if I can't do this maybe enums have an advantage.

UPDATE: Thanks to Roland's help I was able to find the list of sealed classes. but now I want to sort by it: here is what I have so far:

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

but I get the following error at indexOf:

Type inference failed. The value of the type parameter T should be mentioned in input types (argument type, receiver type or expected type). Try to specify it explicitely.

like image 370
j2emanue Avatar asked Jan 28 '23 01:01

j2emanue


1 Answers

You could give your GoodCountries an order property like so:

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

It's not a perfect solution since you have to enumerate by hand, but this way the desired order is guaranteed.

Doing this would simplify your comparison code dramatically:

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

Output:

[Brazil, USA, Germany, China]

Edit: Updated with great idea of Chrispher which made the code even cleaner (having order property in constructor of GoodCountries instead of making it an abstract variable which is overriden by the subclasses).

like image 60
Willi Mentzel Avatar answered May 17 '23 05:05

Willi Mentzel