This code seems to set up a contravariant example: FruitBox can take in Apples or Oranges.
class Fruit(name: String) { }
case class Apple(name: String) extends Fruit(name) {}
case class Orange(name: String) extends Fruit(name) {}
class Box[-T] {
type U >: T
def put(t: U): Unit= {box += t}
val box = scala.collection.mutable.ListBuffer[U]()
}
object test {
val FruitBox = new Box[Fruit]
// Fruit Box takes in everything
FruitBox.put(new Fruit("f1"))
FruitBox.put(new Orange("o1"))
FruitBox.put(new Apple("a1"))
// and Orange Box takes in only oranges
val OrangeBox = new Box[Orange]
//OrangeBox.put(new Apple("o2") ==> Compile Error that makes sense
OrangeBox.put(new Orange("o2"))
// Contra-variant nature is also demonstrated by
val o: Box[Orange] = FruitBox
}
That is all fine...but why does it work ? specifically: 1. When the FruitBox is initialized, why does the "type U >: T" not constrain it to supertypes of Fruit? In spite of that constraint, the FruitBox is able to put the subtypes if Fruit ( oranges and apples )...how?