The +
symbol marks a type parameter as covariant - here we say that "Producer
is covariant on A
":
trait Producer[+A] {
def produce: A
}
A covariant type parameter can be thought of as an "output" type. Marking A
as covariant asserts that Producer[X] <: Producer[Y]
provided that X <: Y
. For example, a Producer[Cat]
is a valid Producer[Animal]
, as all produced cats are also valid animals.
A covariant type parameter cannot appear in contravariant (input) position. The following example will not compile as we are asserting that Co[Cat] <: Co[Animal]
, but Co[Cat]
has def handle(a: Cat): Unit
which cannot handle any Animal
as required by Co[Animal]
!
trait Co[+A] {
def produce: A
def handle(a: A): Unit
}
One approach to dealing with this restriction is to use type parameters bounded by the covariant type parameter. In the following example, we know that B
is a supertype of A
. Therefore given Option[X] <: Option[Y]
for X <: Y
, we know that Option[X]
's def getOrElse[B >: X](b: => B): B
can accept any supertype of X
- which includes the supertypes of Y
as required by Option[Y]
:
trait Option[+A] {
def getOrElse[B >: A](b: => B): B
}