【问题标题】:function vararg parameter for Enum<T> in kotlinkotlin 中 Enum<T> 的函数 vararg 参数
【发布时间】:2021-04-09 11:20:22
【问题描述】:

我有以下枚举,其中每个枚举类实现RatingValue 接口,每个伴随对象实现RatingValues&lt;T&gt; 接口

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

        companion object : RatingValues<Clarity>
    }

    enum class Colour(override val value: Int) : RatingValue {
        EXCELENT(10),
        VERY_GOOD(8),
        GOOD(6),
        FAIR(4),
        POOR(2);

        companion object : RatingValues<Colour>
    }

RatingValues接口有ratings()方法,定义为扩展:

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

我想实现一个方法,它采用 RatingValue 枚举的可变参数参数,可以像这样调用

val cumulativeRating = cumulate(Colour, Clarity)

我的第一个想法是编写以下内容,但是由于 RatingValues 的泛型类型参数 T 明显不同,因此失败了

private inline fun  <reified T> cumulate(vararg ratings: RatingValues<T>) : List<Int> where T: RatingValue, T : Enum<T>  {
        return ratings
            .map(RatingValues<T>::ratings)
            .fold(listOf(0, 0, 0, 0, 0)) { x, y -> x.zip(y, Int::plus) }
}

【问题讨论】:

    标签: kotlin generics enums variadic-functions


    【解决方案1】:

    可以接受具有不同T 的可变参数RatingValues&lt;T&gt; 的方法是:

    private fun cumulate(vararg ratings: RatingValues<*>): List<Int> {
        return ratings
            .map { it.ratings() }
            .reduce { x, y -> x.zip(y, Int::plus) } //same semantics, but more concise and performant
    }
    

    问题是由于类型擦除,有关T 的实际类型的信息会丢失,因此它将被具体化为Object,你会得到相当神秘的运行时错误:java.lang.NoSuchMethodError: java.lang.Object.values()[Ljava/lang/Object;(在我看来, 编译器一开始就不应该编译它,但这不是重点)。

    恐怕,您必须将RatingValues 接口中的ratings() 方法定义为非泛型,并在每个枚举的伴随对象中实现它以使其正常工作:

    interface RatingValues<T> where T : RatingValue, T : Enum<T> {
        fun ratings(): List<Int>
    }
    
    inline fun <reified T> RatingValues<T>.ratingsForEnums(): List<Int> where T : RatingValue, T : Enum<T> =
        enumValues<T>().map { it.value }
    
    enum class Clarity(override val value: Int) : RatingValue {
        EXCELENT(5),
        VERY_GOOD(4),
        GOOD(3),
        FAIR(2),
        POOR(1);
    
        companion object : RatingValues<Clarity> {
            override fun ratings() = ratingsForEnums()
        }
    }
    
    enum class Colour(override val value: Int) : RatingValue {
        EXCELENT(10),
        VERY_GOOD(8),
        GOOD(6),
        FAIR(4),
        POOR(2);
    
        companion object : RatingValues<Colour> {
            override fun ratings() = ratingsForEnums()
        }
    }
    

    【讨论】:

    • 感谢您的详尽解释。我认为在这种情况下,我宁愿坚持将实际评级列表作为参数传递给cumulate,因为我想保持枚举定义干净(实际上还有很多这样的枚举)。然而,这几乎证实了我对为什么不可能的怀疑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-09
    • 1970-01-01
    • 2021-08-31
    • 1970-01-01
    • 2019-10-16
    相关资源
    最近更新 更多