1

Suppose I have many enum classes like the following:

enum class Hero(val alias: String) {
    SUPERMAN("Clark Kent"),
    BATMAN("Bruce Wayne");

    companion object {
        fun fromAlias(value: String): Hero? = Hero.values().find { it.alias.equals(value, true) }
    }
}

enum class Villain(val alias: String) {
    TWO_FACE("Harvey Dent"),
    RIDDLER("Edward Nigma");

    companion object {
        fun fromAlias(value: String): Villain? = Villain.values().find { it.alias.equals(value, true) }
    }
}

I'd like to be able to create a generic interface to handle the fromAlias method in such a way that I can still call it using Hero.fromAlias("Bruce Wayne"). So my enum classes would be simplified to something like:

enum class Hero(override val alias: String): AliasedEnum<Hero> {
    SUPERMAN("Clark Kent"),
    BATMAN("Bruce Wayne");
}

enum class Villain(override val alias: String): AliasedEnum<Villain> {
    TWO_FACE("Harvey Dent"),
    RIDDLER("Edward Nigma");
}

I attempted to incorporate the answer from Kotlin define interface for enum class values method, but couldn't see a way to access the enum values() from the companion object in the interface. Is there a clean way to do what I am wanting?

1 Answer 1

9

You can quite easily do this by using the fact that companion object objects can extend other classes.

Pretty much any solution will require two different parts, since you need:

  1. A common interface that provides any data required for the function, so it is available regardless of the actual implementation.
  2. A way to attach the shared function to the companion object for <Class>.function access. This can either be an abstract class with the required implementation or a marker class with the implementation as an extension function.

In the end the "cleanest" solution would probably be this:

// Attaching point for the extension function which provides the answer
interface EnumCompanion<T : Enum<T>>

// Marker interface to provide the common data
interface WithAlias {
    val alias: String
}

inline fun <reified T> EnumCompanion<T>.fromAlias(
    value: String
): T? where T : Enum<T>, T : WithAlias {
    return enumValues<T>().find { it.alias == value }
}

// Define the enums and attach the helper to their companion object
enum class Hero(override val alias: String) : WithAlias {
    SUPERMAN("Clark Kent"),
    BATMAN("Bruce Wayne");

    companion object : EnumCompanion<Hero>
}

enum class Villain(override val alias: String) : WithAlias {
    TWO_FACE("Harvey Dent"),
    RIDDLER("Edward Nigma");

    companion object : EnumCompanion<Villain>
}

fun main() {
    println(Hero.fromAlias("Bruce Wayne"))
    println(Villain.fromAlias("Edward Nigma"))
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.