Extension function for enums of the same shape

I have several enums with the same shape:

enum class MyEnum(val id: Int) {
  A(1), B(2);
 
  companion object {
    fun findById(id: Int) = values().first { it.id == id }
  }
}

All these enums have an explicit ID (for data stability reasons) and share the same findById method.

Do you see a possibility to make an extension function findById (without using reflection)?

You could use an inferface for that:

interface Identifiable {
	val id: Int
}

enum class MyEnum(override val id: Int) : Identifiable  {
	A(1), B(2)
}

inline fun <reified T> findById(id: Int): T? where T : Enum<T>, T : Identifiable {
	return enumValues<T>().find { it.id == id }
}

fun main() {
	println(findById<MyEnum>(id = 1)) // A
	println(findById<MyEnum>(id = 2)) // B
	println(findById<MyEnum>(id = 3)) // null
}

or you can also do this to make the function more hermetic:

fun <T> findById(clazz: Class<T>, id: Int): T? where T : Enum<T>, T : Identifiable {
	return clazz.enumConstants.find { it.id == id }
}

inline fun <reified T> findById(id: Int): T? where T : Enum<T>, T : Identifiable {
	return findById(T::class.java, id)
}
2 Likes

Thanks, that looks already pretty good. Do you see any chance to make an extension function from it? At least in my trials it didn’t work (in the sense of passing the compiler …).

Unfortunately this isn’t possible at the moment. You can’t add members to the existing types themselves, only to their instances. This isn’t possible:

inline fun <reified T> T.Companion.findById(id: Int): T? where T : Enum<T>, T : Identifiable {
	return findById(T::class.java, id)
}

fun main() {
	MyEnum.findById(id = 1)
}

On the other hand this is possible, but not very useful in your case:

fun MyEnum.Companion.findById(id: Int): MyEnum? = MyEnum.values().find { it.id == id }

fun main() {
	MyEnum.findById(id = 1)
}
1 Like