This is an Enum class:
enum class Foo(rawValue: Int) {
FIRST(1),
SECOND(2)
}
I can use companion object
to retrieve the type by raw value:
enum class Foo(rawValue: Int) {
FIRST(1),
SECOND(2)
companion object {
fun getCaseBy(value: Int): Foo? {
return entries.firstOrNull { it.rawValue == value }
}
}
}
Then use it like:
Foo.getCaseBy(1)
Any way using generic type to put this getCaseBy
into interface
? Then there's no need to add such method for every enum?
Something like:
interface EnumInterface<T> {
companion object {
fun getCaseBy(value: E): T? {
return T.entries.firstOrNull { it.rawValue == value }
}
}
}
enum class Foo(rawValue: Int): EnumInterface(Foo::class) {
FIRST(1),
SECOND(2)
}
T
is restricted to Enum
class, E
is retrieve T
's rawValue type.Enum
type can confirm this interface EnumInterface
Is this possible?
This is a cleaner implementation inspired by Louis Wasserman's comment.
import kotlin.reflect.KClass
abstract class EnumGetter<E : Enum<E>, V>(private val enumClass: KClass<E>) {
abstract fun predicate(e: E, value: V): Boolean;
fun getEnum(value: V): E? {
return enumClass.java.enumConstants.firstOrNull { predicate(it, value) }
}
}
enum class Foo(val rawValue: Int) {
FIRST(1),
SECOND(2);
companion object : EnumGetter<Foo, Int>(Foo::class) {
override fun predicate(e: Foo, value: Int): Boolean {
return e.rawValue == value
}
}
}
fun main() {
println(Foo.getEnum(1))
println(Foo.getEnum(2))
println(Foo.getEnum(3))
}
If you think overriding predicate
function is too troublesome, you can apply the same method as in the old version below to create an annotation for the key field.
This is the old version.
You can implement this via reflection.
import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.memberProperties
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class Key
inline fun <reified E : Enum<E>> getValue(enumObject: E): Any? {
val field = E::class.memberProperties.first { it.hasAnnotation<Key>() }
return field.get(enumObject)
}
inline fun <reified E : Enum<E>> getValueBy(value: Any?): E {
return enumValues<E>().first { getValue<E>(it) == value }
}
enum class Foo(@Key val rawValue: Int) {
FIRST(1),
SECOND(2);
}
fun main() {
println(getValueBy<Foo>(2));
}
Since enum class can have various property names, the annotation Key
is used to mark the property used for comparing.
Note that if no property is annotated, getValue
will throw a NoSuchElement
error.