首页 > 解决方案 > 与迭代 Kotlin 中枚举值的泛型方法的接口

问题描述

假设这些接口

interface RatingValue {
    val value: Int
}

interface RatingValues {
    fun ratings() : List<Int>
    fun cumulativeRating() : Int = ratings().sum()
}

还有一堆类似于这个的枚举

enum class MarkFoo(override val value: Int) : RatingValue {
        EXCELENT(5),
        VERY_GOOD(4),
        GOOD(3),
        FAIR(2),
        POOR(1);

        companion object : RatingValues {
            override fun ratings() = values().map(MarkFoo::value)
        }
    }

有没有办法将 rating() 函数的实现一般提取到 RatingValues 接口中,以避免在所有枚举的同伴中具有基本相同的完全相同的实现?每个枚举都将实现 RatingValue,并且每个枚举都有一个实现 RatingValues 的同伴

标签: kotlingenericsenumsinterface

解决方案


您需要生成RatingValues接口以访问它Enum<T>的方法RatingValue。但是由于接口甚至它们的虚拟成员都不允许具体化泛型,因此您必须将这些方法转换为扩展:

interface RatingValues<T>
inline fun <reified T> RatingValues<T>.ratings(): List<Int> where T : RatingValue, T : Enum<T> = enumValues<T>().map { it.value }
inline fun <reified T> RatingValues<T>.cumulativeRating(): Int where T : RatingValue, T : Enum<T> = ratings().sum()

用法:

enum class MarkFoo(override val value: Int) : RatingValue {
    EXCELENT(5),
    VERY_GOOD(4),
    GOOD(3),
    FAIR(2),
    POOR(1);

    companion object : RatingValues<MarkFoo>
}

问题是这些方法不能也被声明为抽象接口成员,因为在这种情况下扩展会被遮蔽。所以他们现在只能在这个接口上调用特定T的s(枚举,实现RatingValue接口)并且不能被覆盖。对于其他类型的接口将是空的,没有任何方法可以覆盖:

class X(override val value: Int) : RatingValue
class XX : RatingValues<X> {
    override fun ratings(): List<Int> { /*...*/ } //Impossible now
    override fun cumulativeRating(): Int { /*...*/ } //Impossible now
}

val res = XX.ratings() // Will not compile now since X is not enum

如果您也打算以上述方式使用此接口,那么您必须像以前一样声明接口,只添加一个扩展名但名称不同(如ratingsForEnums),并在每次为枚举实现此接口时添加一些样板:

interface RatingValues<T> {
    fun ratings(): List<Int>
    fun cumulativeRating(): Int = ratings().sum()
}

inline fun <reified T> RatingValues<T>.ratingsForEnums(): List<Int> where T : RatingValue, T : Enum<T> = enumValues<T>().map { it.value }

enum class MarkFoo(override val value: Int) : RatingValue {
    EXCELENT(5),
    VERY_GOOD(4),
    GOOD(3),
    FAIR(2),
    POOR(1);

    companion object : RatingValues<MarkFoo> {
        override fun ratings() = ratingsForEnums()
    }
}

T如果您打算使用此接口仅与枚举形成对比,那么在接口级别上显式地施加限制也是有意义的:

interface RatingValues<T> where T : RatingValue, T : Enum<T>

推荐阅读