Bla*_*son 12 generics scala type-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)
}
我知道type参数T不能在append方法中使用,因为它违反了规则但是如果我说一个Customer类和子类Student我仍然可以创建类型U Student.
我可以看到这有效,但为什么这不违反规则?我可以理解,如果我有一个Students 列表然后添加了一个Customer我只能返回一个Customers 列表,因为它不允许Customer分配给a,Student因为它是父类型.但为什么我可以使用Student?
我错过了什么?
谢谢布莱尔
Gle*_*est 13
你的班级提供2个涉及T的操作:
施工
nextImmutableArray = new ImmutableArray(nextT, priorImmutableArray)
由于此操作,类型参数T必须是共变量:+ T.这允许您使用参数设置构造为类型的对象(T或T的子类型).
想一想:通过加入瓦伦西亚橙来构建一系列橘子是有效的.
组合
nextImmutableArray.append(newItemTorAncestor)
此方法不会附加到您的数据结构.它有两个独立的元素(你的数组实例这和一个额外的对象),并结合新构造的阵列中的他们.您可以考虑将方法名称更改为appendIntoCopy.更好的是,您可以使用名称+.但要最正确并与Scala约定保持一致,最好的名称是:+.
当你问一个特定的问题时,为什么我会对一个'随机'方法名称感到困惑?
因为该方法的精确性质决定了返回的数据结构是否是(a)非变体与T(b)共变体与T(c)反变体与T.
当你结合数组和元素,新创建的数据结构必须有一个类型参数是共同的祖先类型的超类型.否则它不能包含原始元素.通常,当执行"a:+ b"时,其中A是数组[A],b是类型B,结果数据结构是数组[Some_SuperType_Of_Both_A_and_B].
想想:如果我从一系列橘子开始,然后添加柠檬,我最终会得到一系列柑橘类水果(不是橙子,脐橙,也不是柠檬).
方法规则(严格输入,适应输出):
在附加的情况下:以T开头,输出数据结构=对比变量为T,类型S使用T作为下限,因此输入参数=与S的共变量.这意味着如果T1是T2的子类型则ImmutableArray [T1]是ImmutableArray [T2]的子类型,它可以在预期后者的任何地方被替换,所有方法都遵循Liskov的替换原则.
Jat*_*tin 10
第一个问题:
我知道类型参数T不能在append方法中使用,因为它违反了规则
好吧,它可以使用.S >: T简单地说,如果你传入一个S等于T或等于它的类型,那么S将被使用.如果你传递一个sublevel类型T然后T将被使用.
scala> class Animal
defined class Animal
scala> class Canine extends Animal
defined class Canine
scala> class Dog extends Canine
defined class Dog
scala> new ImmutableArray[Canine](new Canine)
res6: ImmutableArray[Canine] = ImmutableArray@a47775
scala> res6.append(new Animal)
res7: ImmutableArray[Animal] = ImmutableArray@1ba06f1
scala> res6.append(new Canine)
res8: ImmutableArray[Canine] = ImmutableArray@17e4626
scala> res6.append(new Dog)
res9: ImmutableArray[Canine] = ImmutableArray@a732f0
上面的做法res6.append(new Dog)仍然给你类型犬的ImmutableArray.如果你想某种方式它是完全合理的,因为添加Dog to Canine Array仍将保留阵列Canine.但是将动物添加到犬阵列会使它成为动物,因为它不再是完美的犬(可以是磨牙或其他东西).
这是一个很好的例子,说明为什么通常知道反变体类型声明使其适合于写入(您的情况)和读取的协方差.
在你的榜样,我觉得混乱可能是因为你比较S >: T于S super T(从Java世界).随着S super T你必然会有参数类型是超一流的T,它不会让你传递一个参数,即子类型T.在scala中,编译器会处理这个问题(感谢类型推断).