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:
First question:
I understand that the type parameter T can not be used in the append method as it violates the rules
Well it can be used. S >: T
simply means that if you pass in a type S
that is equal to T
or its parant, then S
will be used. If you pass a type that is sublevel to T
then T
will be used.
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
Above doing res6.append(new Dog)
still gives you ImmutableArray of type Canine. And if you think in a way it makes complete sense as adding Dog to Canine Array will still keep the array Canine. But adding Animal to Canine Array makes it Animal as it can no longer be perfectly canine (can be molar or something).
This is a perfect example on why it is usually known that contra-variant type declaration make it perfect for writes (your case) and co-variance for reads.
In your example, I think the confusion might be because you are comparing S >: T
to S super T
(from java world). With S super T
you are bound to have the argument type that is Super class of T
and it does not allow you to pass an argument that is sub-type to T
. In scala, the compiler takes care of this (thanks to type-inference).