我有一个通用的密封类,用于表示单个值或值对(在特定事件之前和之后分割):
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>()
我想定义在Splittable
上通用(可参数化)的数据类,以便该类的属性必须全部为Single或全部为Split。做到:
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)
)
但是我错了,因为它允许无效的组合:
val WRONG = Totals(
people = Single(3),
things = Split(4, 40)
)
此外,如果我的班级有多个基本类型,例如Int
和Boolean
怎么办?如何编写通用签名?
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)
)
数据类声明给出错误:
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>
^
因此,看来我无法传递类型较高的类型作为类型参数。闷闷不乐。
我可以将两个泛型解耦:
data class TotalsMore<SInt : Splittable<Int>,
SBoolean: Splittable<Boolean>>(
val people : SInt,
val things : SInt,
val happy : SBoolean
)
这有效,但更明显的是,您可以混合和匹配我想禁止的“单人”和“拆分”:
val WRONG = TotalsMore(
people = Single(3),
things = Single(4),
happy = Split(true, false)
)
我希望这些数据类的每个对象要么全部由Single值组成,要么由所有Split值组成,而不是混合搭配。
我可以使用Kotlin的类型来表达它吗?
答案 0 :(得分:2)
您的Totals
类需要一个泛型类型参数,但是您无需在示例的构造函数中指定一个。工作方式是类型推断:Kotlin编译器从其他参数中找出泛型。在第一个Single
示例中混合使用Split
和WRONG
的原因是,编译器会看到两个参数并从中推断出公共超类型。因此,您实际上是在构建Totals<Splittable<Int>>
。
如果要明确指定子类型,则无法混合:
val WRONG = Totals<Single<Int>>(
people = Single(3),
things = Split(4, 40) /** Type inference failed. Expected type mismatch: inferred type is Split<Int> but Single<Int> was expected */
)
因此,您想要做的是接受Splittable
的子类而不是Splittable
本身作为通用参数。
您可以通过为子类添加一个附加接口,并为附加一个where
子句的generic constraint来实现:
sealed class Splittable<T>
interface ConcreteSplittable
data class Single<T>(val single: T) : Splittable<T>(), ConcreteSplittable
data class Split<T>(val before: T,
val after : T) : Splittable<T>(), ConcreteSplittable
data class Totals<SInt : Splittable<Int>>(
val people : SInt,
val things : SInt
) where SInt : ConcreteSplittable
val t1 = Totals<Single<Int>>(
people = Single(3),
things = Single(4)
)
val t2 = Totals(
people = Split(3, 30),
things = Split(4, 40)
)
val WRONG = Totals( /** Type parameter bound for SInt in constructor Totals<SInt : Splittable<Int>>(people: SInt, things: SInt) where SInt : ConcreteSplittable is not satisfied: inferred type Any is not a subtype of Splittable<Int> */
people = Single(3),
things = Split(4, 40)
)
关于第二部分,我认为这是不可能的。如您所述,类型参数不允许使用类型参数。
不幸的是,您也不能引入第三类型参数S
并将SInt
和SBool
限制为普通类型S
和Splittable<Int>
或分别Splittable<Bool>
。
data class TotalsMore<S, SInt, SBool>
(
val people : SInt,
val things : SInt,
val happy : SBool
) where S : ConcreteSplittable,
SInt : S,
SInt : Splittable<Int>, /** Type parameter cannot have any other bounds if it's bounded by another type parameter */
SBool : S,
SBool : Splittable<Boolean> /** Type parameter cannot have any other bounds if it's bounded by another type parameter */
您可以做的是创建“安全”类型的别名,如下所示:
data class TotalsMore<SInt : Splittable<Int>, SBool : Splittable<Boolean>> (
val people : SInt,
val things : SInt,
val happy : SBool )
typealias SingleTotalsMore = TotalsMore<Single<Int>, Single<Boolean>>
typealias SplitTotalsMore = TotalsMore<Split<Int>, Split<Boolean>>
val s = SingleTotalsMore(
people = Single(3),
things = Single(4),
happy = Single(true) )
创建混合的TotalsMore
仍然可能。