Tobse
Tobse

Reputation: 1416

Kotlin extension for next Enum value without reflection

I wrote a Kotlin extension which adds next() for enum values. But is there an better way to do that?

fun <T : Enum<*>> T.next(): T {
    val values = this::class.java.getEnumConstants()
    return if (this.ordinal < values.size - 1) 
            values[this.ordinal + 1] 
        else 
            values[0]
}
enum class Color {Red, Yellow, Green}
Color.Red.next() //Yellow
  1. Can I achieve that without reflections?
  2. If not, how to do that with Kotlins reflection?

The Idea is to iterate from enum value to the next. But only for a specific value, not for whole enum values() list. The thing I want to prevent is to add the following to every enum type:

fun next() = if (this.ordinal == Color.values().size - 1) 
        Color.values()[0] 
    else 
        Color.values()[this.ordinal + 1]

Upvotes: 9

Views: 6161

Answers (2)

Codelaby
Codelaby

Reputation: 2873

Based with @hotkey

Add extension functions for: next, nextOrLast , previous, previousOrFirst

inline fun <reified T: Enum<T>> T.next(): T {
    val values = enumValues<T>()
    val nextOrdinal = (ordinal + 1) % values.size
    return values[nextOrdinal]
}

inline fun <reified T: Enum<T>> T.nextOrLast(): T {
    val values = enumValues<T>()
    val nextOrdinal = if ((ordinal + 1) >= values.size) ordinal else (ordinal+1)
    return values[nextOrdinal]
}


inline fun <reified T: Enum<T>> T.previousOrFirst(): T {
    val values = enumValues<T>()
    val previousOrdinal = if ((ordinal - 1) < 0) ordinal else (ordinal-1)
    return values[previousOrdinal]
}

inline fun <reified T: Enum<T>> T.previous(): T {
    val values = enumValues<T>()
    val previousOrdinal = if ((ordinal - 1) < 0) values.size -1 else (ordinal-1)
    return values[previousOrdinal]
}



enum class Color { Red, Yellow, Green }

fun main() {
    val colors = generateSequence(Color.Yellow) { it.previous() }.take(10)
    colors.forEach(::println)
}

Demo of this code

Upvotes: 2

hotkey
hotkey

Reputation: 147961

You can do that without reflection in Kotlin 1.1 (currently, you can try the 1.1 RC build) using the enumValues<T>(), which does not use the reflection inside, because it is inlined at call sites instead, and the corresponding enum type is placed in the resulting code at compile-time. Example:

inline fun <reified T: Enum<T>> T.next(): T {
    val values = enumValues<T>()
    val nextOrdinal = (ordinal + 1) % values.size
    return values[nextOrdinal]
}

(demo of this code) | The inline and reified are required because enumValues<T>() has reified T.

An alternative that works with Kotlin 1.0.6 (but still uses getDeclaringClass() from Java reflection):

fun <T: Enum<T>> T.next(): T {
    val values = declaringClass.enumConstants
    val nextOrdinal = (ordinal + 1) % values.size
    return values[nextOrdinal]
}

Upvotes: 21

Related Questions