Scala协方差与下界类型解释 [英] Scala Covariance and Lower Type Bounds Explanation

查看:114
本文介绍了Scala协方差与下界类型解释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图使用下界来创建新的不可变类型的方法,让我的头在协方差方面

 类ImmutableArray [ + T](item:T,existing:List [T] = Nil){
private val items = item :: existing

def append [S>:T](value: S)= new ImmutableArray [S](value,items)
}

类型参数 T 不能在append方法中使用,因为它违反了规则,但是如果我有一个 Customer 类和子类学生我仍然可以使类型 U 学生



我可以看到这个作品,但为什么这不违反规则?我可以理解,如果我有学生 s的列表,然后添加客户我只能返回<由于不允许将客户分配给学生,因此code> Customer 因为它是父类型。但为什么我可以使用学生



我缺少什么?



感谢布莱尔

解决方案

您的课程提供2个涉及T的操作:


  1. 构造

      nextImmutableArray = new ImmutableArray(nextT,priorImmutableArray)

    由于这种操作,类型参数T必须是协变式:+ T。这允许您使用参数集来构造一个类型为(T或T的子类型)的对象。



    想一想:通过添加一个 Valencia Orange来构建橙子数组是有效的。


  2. 组合

      nextImmutableArray.append(newItemTorAncestor)
    code>

    此方法不会附加到您的数据结构。它需要两个独立的元素(数组实例 this 和一个额外的对象),并将它们结合在一个新构建的数组中。您可以考虑将您的方法名称更改为 appendIntoCopy 。更好的是,您可以使用名称 + 。但为了最好地正确和符合Scala惯例,最好的名字应该是:+







    为什么我会在一个'随机'因为该方法的确切性质决定了返回的数据结构是否是(a)T(b)与T(c)逆变换与T的非变量。


    • 开始于:ImmutableArray [T] - 包含类型T(或子类型)

    • 与S类型的对象结合使用。

    • 结果:ImmutableArray [S]

    • 如果S被允许是T的适当子类型(超出T本身),那么新数组不能包含原始T型元素!

    • 如果S的类型是T或T的超类型,那么一切都很好 - 可以包含原始元素和新元素!



    结合数组和元素时,新创建的数据结构必须具有一个类型参数共同的祖先类型的超类型。否则,它不能包含原始元素。通常,当您执行a:+ b时,其中A是数组[A],而b是类型B,则生成的数据结构是数组[Some_SuperType_Of_Both_A_and_B]。



    想一想:如果我从一组桔子开始,然后添加一个柠檬,我最终会得到一串柑橘类水果 (不是橘子,脐橙,也不是柠檬)。

  3. b


    $ b 方法规则(严格的输入,容纳输出)


    • a)输入参数提供了一个元素来插入(突变): Co-Variant

    • a)输出参数数据结构: Contra-Variant
    • c)输出参数,在合并后返回数据结构: Contra-Variant < c)使用类型作为下界:翻转方差(对T的反变换=对S的同变异,具有下界T)



    如果追加:从T开始,输出数据结构= S使用T作为下界,所以输入参数=与S.T的同变异他的意思是,如果T1是T2的一个子类型,则ImmutableArray [T1]是ImmutableArray [T2]的子类型,并且可以在任何后者被预期的情况下替换,所有方法都遵循Liskov的替换原则。

    I am trying to get my head around covariance in respect with methods creating new immutable types using lower bounds

    class ImmutableArray[+T](item: T, existing: List[T] = Nil) {  
      private val items = item :: existing
    
      def append[S >: T](value: S) = new ImmutableArray[S](value, items)
    }
    

    I understand that the type parameter T can not be used in the append method as it violates the rules but if I have say a Customer class and sub class Student I can still make the type U Student.

    I can see this works but why is this not a violation of the rules? I could understand if I had a list of Students and then added a Customer I could only return a list of Customers due to not allowing a Customer to be assigned to a Student as it is a parent type. But why can I use Student?

    What am I missing?

    Thanks Blair

    解决方案

    Your class offers 2 operations involving T:

    1. Construction

      nextImmutableArray = new ImmutableArray(nextT, priorImmutableArray)
      

      Because of this operation, the type parameter T must be co-variant: +T. That allows you to construct with the parameter set to an object of type (T OR a subtype of T).

      Think: it's valid to construct an array of Oranges by including a Valencia Orange.

    2. Combination

      nextImmutableArray.append(newItemTorAncestor)
      

      This method doesn't append to your data structure. It takes two independent elements (your array instance this and an extra object) and it combines them within a newly constructed array. You could consider changing your method name to appendIntoCopy. Even better, you could use the name +. But to be most correct and consistent with Scala conventions, the best name would be :+ .

      Why am I waffling on about a 'random' method name, when you asked a specific question???

      Because precise nature of the method determines whether the returned data structure is (a) non-variant with T (b) co-variant with T (c) contra-variant with T.

      • Start with: ImmutableArray[T] - contains type T (or subtypes)
      • Combine with: Object of type S.
      • Result: ImmutableArray[S]
      • If S was allowed to be a proper subtype of T (beyond T itself), then the new array can't contain original elements of type T!
      • If S is of type T or a supertype of T, then all is good - can contain original elements, plus new element!

      When you combine arrays and elements, the newly created data structure must have a type parameter that is a supertype of the common ancestor type. Otherwise it couldn't contain the original elements. In general when you carry out "a :+ b", where A is an Array[A] and b is of type B, the resulting data structure is Array[Some_SuperType_Of_Both_A_and_B].

      Think: if I start with an array of Oranges, then add a Lemon, I end up with an array of Citrus Fruit (not Oranges, Navel Oranges, nor Lemons).


    Method Rules (strict on input, accomodating on output):

    • a) input parameter provides an element to insert (mutation): Co-Variant
    • a) output parameter returns an element from data structure: Contra-Variant
    • c) output parameter, returns data structure after combining: Contra-Variant
    • c) Use type as a lower bound: "Flip" variance ("Contra-variant to T" = "Co-Variant to S, which has lower-bound T")

    In case of append: Start with T, Output Data Structure = Contra-Variant to T, Type S uses T as a lower-bound, so Input Parameter = Co-Variant with S. This means that if T1 is a subtype of T2 then ImmutableArray[T1] is a subtype of ImmutableArray[T2] and that it can be substituted wherever the latter is expected, with all methods following Liskov's substitution principle.

    这篇关于Scala协方差与下界类型解释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆