首页 > 解决方案 > Kotlin:在通用函数中访问伴随数据

问题描述

我在 Kotlin 中有一个列出枚举值的简单任务:

interface DisplayableEnum<E: Enum<E>> {
    val displayValue: String
}

inline fun <reified T> printAllValues(selected: T?) where T: Enum<T>, T: DisplayableEnum<T> {
    println("All Values:")
    enumValues<T>().forEach {
        println(it.displayValue)
    }
    selected?.let { println("\nSelected: ${it.displayValue}") }
}


////// USAGE

enum class MyEnum(override val displayValue: String): DisplayableEnum<MyEnum> {
    A("value is A"),
    B("value is B")
}

fun main() {
    // with a selected value
    f(MyEnum.A)

    // without a selected value
    f(null as MyEnum?)
}

现在想象一下,我传递给的所有枚举printAllValues也应该有一个名为defaultValue. 如果MyEnum我会这样写:

enum class MyEnum(override val displayValue: String): DisplayableEnum<MyEnum> {
    A("value is A"),
    B("value is B");

    companion object {
        val defaultValue = A
    }
}

所以我的问题是:有没有办法在 Kotlin 中定义这样的合同?

理想情况下,我想以某种方式在接口中定义该合同,就像DisplayableEnum上面一样,然后以某种方式使用它printAllValues

inline fun <reified T> printAllValues(selected: T) where T: Enum<T>, T: DisplayableEnum<T> {
    println("All Values:")
    enumValues<T>().forEach {
        println(it.displayValue)
    }
    selected?.let { println("\nSelected: ${it.displayValue}") }
    println("Default value: ${T.defaultValue???}"
}

我不想要的一件事是使用 non-companion defaultValue,我总是必须手动将它传递给函数(但是为什么如果类型包含所有信息?)或者,如果设为非伴随:

interface DisplayableEnum<E: Enum<E>> {
    val displayValue: String
    val defaultValue: E
}

然后通过一个对象访问它——做一些丑陋的事情,比如enumValues<T>().first().defaultValue.

我想知道 Kotlin 在这种情况下是否有解决方案。

标签: kotlingenericsenums

解决方案


在伴生对象中定义抽象属性是不可能的。所以应该直接在接口中定义。

这里棘手的部分是在没有编译器警告的情况下实现这个接口:

enum class MyEnum(override val displayValue: String) : DisplayableEnum<MyEnum> {
    A("value is A"),
    B("value is B");

    override val defaultValue: MyEnum by lazy { A }
}

如果enumValues<T>().first().defaultValue你觉得难看,请将其包装到辅助函数中:

inline fun <reified T> enumDefaultValue(): T where T : Enum<T>, T : DisplayableEnum<T> =
    enumValues<T>().first().defaultValue

//Usage:
println("Default value: ${enumDefaultValue<T>()}")

推荐阅读