Reputation: 682
I am new with scala. I am trying to figure out how the whole contravariance relationship works. I understand the concept of covariance and invariant and I also know how I would implement them in practise. I also understand the concept of contravariance (the reverse of covariance) and how it is implemented in the Function1 trait in Scala. It gives you an abstraction without redefining Function1 implementations for different classes. But, I still don’t get it completely, strange? Now, I am almost there… how can I solve the following problem with contravariance:
class GarbageCan[-A] {
def doSomething(a: A): Unit ={
// do something with 'a' of subtype that is not possible with the supertype
}
}
def setGarbageCanForPlastic(gc: GarbageCan[PlasticItem]): Unit = {
}
The above example is extracted from http://blog.kamkor.me/Covariance-And-Contravariance-In-Scala/. A really good explanation concerning this subject. The hierarchy is as follows: Item (base class) -> PlasticItem (subclass) -> PlasticBottle (subclass of subclass)
The setGarbageCanForPlastic function accepts a GarbageCan with the type of PlasticItem. Because the Parameterized type is contravariant, the following statement is completely legal:
setGarbageCanForPlastic(new GarbageCan[Item])
Now, the doSomething function accepts a Type parameter which is contravariant. How can I work with this type, if I don’t know if the type is a Base class “Item” or subclass “PlasticItem”? I can do something which is permissible within the subclass and not in the base class. If this was an covariant parameter, this would be no problem, a subclass inherits everything from the base class.
I lost it right?... Hope somebody can help me out.
Upvotes: 3
Views: 150
Reputation: 841
First of all, the doSomething
method actually cannot do anything other than essentially discarding a
since it does not know what A
is. To make it more useful, you would need a bound like class GarbageCan[-A <: Item]
.
Now, suppose setGarbageCanForPlastic(gc)
calls gc.doSomething(new PlasticItem)
. Since A
in GarbageCan[A]
is contravariant, we have GarbageCan[Item] <: GarbageCan[PlasticItem] <: GarbageCan[PlasticBottle]
. Indeed, the function call setGarbageCanForPlastic(new GarbageCan[Item]))
is safe as GarbageCan[Item]
's doSomething
can process any Item
including a PlasticItem
, while the call setGarbageCanForPlastic(new GarbageCan[PlasticBottle]))
is unsafe because GarbageCan[PlasticBottle]
's doSomething
may not be able to take a PlasticItem
which is not necessarily a PlasticBottle
.
Upvotes: 1