【问题标题】:Interface with generic method iterating over enum values in Kotlin与迭代 Kotlin 中枚举值的泛型方法的接口
【发布时间】:2021-04-02 23:24:02
【问题描述】:

假设这些接口

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

【问题讨论】:

  • 不幸的是,我不这么认为。 values() 不是 Enum 接口的一部分,您不能创建使用伴生对象作为接收者的扩展函数。

标签: kotlin generics enums interface


【解决方案1】:

你需要生成RatingValues接口来访问Enum&lt;T&gt;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>
}

问题是这些方法不能也被声明为抽象接口成员,因为在这种情况下扩展会被遮蔽。因此,它们现在只能针对特定的Ts(枚举,实现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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多