I have the following Enums where each Enum class implements RatingValue
interface and each companion object implements RatingValues<T>
interface
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
interface has ratings()
method, which is defined as an extention:
inline fun <reified T> RatingValues<T>.ratings(): List<Int> where T : RatingValue, T : Enum<T> = enumValues<T>().map { it.value }
I'd like to implement a method that takes a vararg parameter of RatingValue
enums which would be callable like this
val cumulativeRating = cumulate(Colour, Clarity)
My first idea was to write the following, however that fails since the generic type parameter T for RatingValues is obviously different
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) }
}
Method which could accept vararg RatingValues<T>
with different T
is:
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
}
The problem is that due to type erasure, information about actual type of T
would be lost, so it will be reified as Object
and you'll get rather cryptic runtime error: java.lang.NoSuchMethodError: java.lang.Object.values()[Ljava/lang/Object;
(in my opinion, compiler shouldn't have compiled this in the first place, but that's not the point).
I'm afraid, you' ll have to define ratings()
method in the RatingValues
interface as non-generic and implement it in every enum's companion object to make it work correctly:
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()
}
}