Kotlin:Enum类的通用接口,具有从String映射的静态方法

Kotlin: common interface on Enum class with static method to map from String

假设我有许多如下的枚举类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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) }
    }
}

我希望能够创建一个通用接口来处理fromAlias方法,这样我仍然可以使用Hero.fromAlias("Bruce Wayne")来调用它。因此我的枚举类将简化为:

1
2
3
4
5
6
7
8
9
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");
}

我尝试将Kotlin定义接口的答案合并到枚举类值方法中,但是找不到从接口中的伴随对象访问枚举values()的方法。有什么干净的方法可以做我想要的吗?


通过使用companion object对象可以扩展其他类的事实,您可以轻松地做到这一点。

几乎所有解决方案都需要两个不同的部分,因为您需要:

  • 一个通用接口,提供该功能所需的任何数据,因此无论实际实现如何,该接口都可用。
  • 一种将共享功能附加到companion object以进行<Class>.function访问的方法。这可以是带有所需实现的抽象类,也可以是带有实现的扩展功能的标记类。
  • 最后,"最干净的"解决方案可能是这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // 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"))
    }