I have a generic sealed class, used to represent either single values or pairs of values (split before and after a certain event):
sealed class Splittable<T>
data class Single<T>(val single: T) : Splittable<T>()
data class Split<T>(val before: T,
val after : T) : Splittable<T>()
I would like to define data classes that are generic (parameterizable) over Splittable
, so that the properties of the class must either be all Single or all Split. I thought this would do it:
data class Totals<SInt : Splittable<Int>>(
val people : SInt,
val things : SInt
)
val t1 = Totals(
people = Single(3),
things = Single(4)
)
val t2 = Totals(
people = Split(3, 30),
things = Split(4, 40)
)
But I was wrong, because it allows invalid combinations:
val WRONG = Totals(
people = Single(3),
things = Split(4, 40)
)
Moreover, what if my class has more than one basic type, for instance both Int
and Boolean
? How do I write the generic signature?
data class TotalsMore<S : Splittable>(
val people : S<Int>,
val things : S<Int>,
val happy : S<Boolean>
)
val m1 = TotalsMore(
people = Single(3),
things = Single(4),
happy = Single(true)
)
val m2 = TotalsMore(
people = Split(3, 30),
things = Split(4, 40),
happy = Split(true, false)
)
The data class declaration gives errors:
error: one type argument expected for class Splittable<T>
data class TotalsMore<S : Splittable>(
^
error: type arguments are not allowed for type parameters
val people : S<Int>,
^
error: type arguments are not allowed for type parameters
val things : S<Int>,
^
error: type arguments are not allowed for type parameters
val happy : S<Boolean>
^
So it appears I cannot pass a higher-kinded type as a type parameter. Bummer.
I can decouple the two generics:
data class TotalsMore<SInt : Splittable<Int>,
SBoolean: Splittable<Boolean>>(
val people : SInt,
val things : SInt,
val happy : SBoolean
)
This works, but it makes it even more obvious that you can mix and match Single and Split, which I want to forbid:
val WRONG = TotalsMore(
people = Single(3),
things = Single(4),
happy = Split(true, false)
)
I would like every object of those data classes to either be made of all Single values, or all Split values, not a mix and match.
Can I express it using Kotlin's types?