A TypeScript enum compiles to a plain object of numbers or strings; each Kotlin enum constant is a real object with constructor arguments, properties and even its own override of an abstract function. BookNest's sort orders carry a button label and a sorting rule.
data class Book(val title: String, val price: Double)
enum class SortOrder(val label: String) {
TITLE("Title A-Z") { override fun sort(b: List<Book>) = b.sortedBy { it.title } },
PRICE("Lowest price") { override fun sort(b: List<Book>) = b.sortedBy { it.price } };
abstract fun sort(b: List<Book>): List<Book>
}
fun main() {
val books = listOf(Book("The Quiet Harbor", 14.99), Book("Patterns of the Deep Web", 39.50))
for (o in SortOrder.entries)
println("${o.ordinal} ${o.name} (${o.label}): ${o.sort(books)[0].title}")
println(runCatching { SortOrder.valueOf("price") }.exceptionOrNull()?.message)
}0 TITLE (Title A-Z): Patterns of the Deep Web 1 PRICE (Lowest price): The Quiet Harbor No enum constant SortOrder.price
Every constant has name and a zero-based ordinal. The class has entries, a read-only List of all constants (stable since Kotlin 1.9.0 and preferred over values(), which allocates a new array per call), and valueOf(), which throws for an unknown or wrongly cased name. A semicolon ends the constants when members follow. Never persist ordinal, which shifts when you insert a constant. Persist name, and read it back with entries.firstOrNull { it.name == saved } plus a default in case the name no longer exists.