We have tons of enums which encode a mapping from ...
# announcements
n
We have tons of enums which encode a mapping from a numeric value (stored in a DB) to a name. I read about "enum with value" and I have some nice compact code like
Copy code
enum class Type(val value: Int) {
    INVALID(-1), SIMPLE(1), COMPLEX(2);
    companion object {
        private val map = values().associateBy(Type::value)
        fun fromInt(value: Int) = map.getOrDefault(value, INVALID)
        fun fromString(value: String) = try { valueOf(value) } catch (e: IllegalArgumentException) { INVALID }
    }
}
Is there a way to avoid repeating the "companion" part in every enum? Ideally, I would want
enum class Type(code: Int) : Magic(code) { INVALID(-1), SIMPLE(1), COMPLEX(2); }
with also some way to identify the INVALID obejct (or just assume it is always the value associated to
-1
)
d
You could make yourself a compiler plugin. Or you could make a small class for your companion objects to extend, so you don't have to repeat yourself as much.
n
Any idea how to write this "small class"? It needs access to the
value
in the enum elements and the
values()
etc. methods.
d
You can pass on values() in the constructor. Then for value, you can make the enum conform to an interface.
n
Here is what I have so far:
Copy code
interface EnumValue<T> {
    val value: T
}

open class EnumCompanion<T, U>(values: Array<T>, val invalid: T) where T: EnumValue<U> {
    private val map = values.associateBy { it.value }
    fun fromValue(value: U) = map.getOrDefault(value, invalid)
    fun fromName(value: String) = map.values.find { it.toString() == value } ?: invalid
}

enum class Type(override val value: Int) : EnumValue<Int> {
    INVALID(-1), SIMPLE(1), COMPLEX(2);

    companion object : EnumCompanion<Type, Int>(values(), INVALID)
}
comments?
one (now) obvious improvement:
Copy code
private val valueMap = values.associateBy { it.value }
private val nameMap = values.associateBy { it.toString() }
fun fromValue(value: U) = valueMap.getOrDefault(value, invalid)
fun fromName(name: String) = nameMap.getOrDefault(name, invalid)
This still needs to repeat the value type 3 times for every enum: for the constructor, for the base class, and for the companion class.