代码之家  ›  专栏  ›  技术社区  ›  Tobia

更高种类的类型作为类型参数

  •  1
  • Tobia  · 技术社区  · 6 年前

    我有一个通用的密封类,用于表示单个值或成对值(在某个事件之前和之后拆分):

    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 ,因此类的属性必须 要么全部是单一的,要么全部是分裂的。 我以为这样就行了:

    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)
    )
    

    我希望这些数据类的每个对象要么由所有单个值组成,要么由所有拆分值组成,而不是混合匹配。

    我能用Kotlin的类型来表达吗?

    1 回复  |  直到 6 年前
        1
  •  2
  •   Dario Seidl    6 年前

    你的 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 可拆分的 本身作为泛型参数。

    您可以通过子类的附加接口和附加的 generic constraint where -条款:

    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

    推荐文章
    alphacentauri  ·  HOMap实现示例
    10 年前