How can I allow this to accept a String in the `fi...
# codereview
a
How can I allow this to accept a String in the
find
extension in addition to an Int? Is there anything else I can do to optimize as well?
Copy code
infix fun <T> T?.ifNull(block: () -> T?) = this ?: block()

inline fun <reified T> EnumCompanion<T>.find(value: Int): T? where T : Enum<T>, T : EnumValue<T> =
    enumValues<T>().run {
        firstOrNull { (it.value as Int) == value }
            .ifNull { firstOrNull { it.name == "Unknown" } }
            .ifNull { throw Exception("Enum value does not exist") }
    }

interface EnumCompanion<T : Enum<T>>

interface EnumValue<T> {
    val value: T
}

enum class MyEnum(override val value: Int) : EnumValue<Int> {
    Unknown(0),
    First(1),
    Second(2),
    Third(3);

    companion object : EnumCompanion<MyEnum>
}

MyEnum.find(1)
m
You need two type parameters, one for the
Enum
type and one for the
EnumValue
type. Furthermore, a couple of things: • Your
ifNull
could just be the inbuilt
?:
operator • The return type is currently
T?
, but could be
T
, since it will never return null So, the function could look like this:
Copy code
inline fun <reified T, V> EnumCompanion<T>.find(value: V): T where T : Enum<T>, T : EnumValue<V> =
    enumValues<T>().run {
        firstOrNull { it.value == value }
            ?: firstOrNull { it.name == "Unknown" }
            ?: throw Exception("Enum value does not exist")
    }
From a personal perspective, I would not use the matching on
name == "Unknown"
to attempt to find an "Unknown". I would probably have three functions:
Copy code
inline fun <reified T, V> EnumCompanion<T>.find(value: V): T? where T : Enum<T>, T : EnumValue<V> =
    enumValues<T>().firstOrNull { it.value == value }

inline fun <reified T, V> EnumCompanion<T>.findOrError(value: V): T where T : Enum<T>, T : EnumValue<V> =
    find(value) ?: throw Exception("Enum value $value does not exist")

inline fun <reified T, V> EnumCompanion<T>.findOrDefault(value: V, default: T): T where T : Enum<T>, T : EnumValue<V> =
    find(value) ?: default
And I'm not sure about the third, since the
?:
can already express that at the call site. If your domain has well defined "defaults" for enumerations, then you could expand the
EnumCompanion
to contain it, and then use it - something like this:
Copy code
interface EnumCompanion<T : Enum<T>> {
    val default: T
}

enum class MyEnum(override val value: Int) : EnumValue<Int> {
    Unknown(0),
    First(1),
    Second(2),
    Third(3);

    companion object : EnumCompanion<MyEnum> {
        override val default = Unknown
    }
}

inline fun <reified T, V> EnumCompanion<T>.find(value: V): T where T : Enum<T>, T : EnumValue<V> =
    enumValues<T>().firstOrNull { it.value == value } ?: default