kotlin 中 Enum<T> 的函数 vararg 参数
function vararg parameter for Enum<T> in kotlin
我有以下枚举,其中每个枚举 class 实现 RatingValue
接口,每个伴随对象实现 RatingValues<T>
接口
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
枚举的 vararg 参数,可以像这样调用
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) }
}
可以接受具有不同 T
的可变参数 RatingValues<T>
的方法是:
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()
}
}
我有以下枚举,其中每个枚举 class 实现 RatingValue
接口,每个伴随对象实现 RatingValues<T>
接口
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
枚举的 vararg 参数,可以像这样调用
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) }
}
可以接受具有不同 T
的可变参数 RatingValues<T>
的方法是:
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()
}
}