This is a very concise tutorial on covariance and contravariance. In 10 minutes you should understand what these concepts are and how to use them. The examples are in Scala, but apply to Java or C# as well.



Covariance

val apples = List( new Apple(), new Apple())

processList(apples)





def processList(list:List[Fruit]) = {

// read the list

}

val a = Array( new Apple(), new Apple())

processArray(a)





def processArray(array:Array[Fruit]) = {

array(1) = new Orange() // putting an Orange into array of Apples!

}

sealed abstract class List[+A]

checks

Contravariance

Logger[-A]

// isGoodFruit is a func of type Fruit=>Boolean

def

isGoodFruit(f:Fruit) = f.ageDays < 3

val list:List[Apple] = List( new Apple(), new Apple())

list.filter(isGoodFruit) // filter takes a func Apple=>Boolean

Function[Fruit, Boolean]

traits

trait Function[-A,+B]

contravariant in their parameter types

covariant in their return types

Assuming Apple is a subclass of Fruit, covariance lets you treat say List[Apple] as List[Fruit].This seems obvious - indeed, a list of apples is a list of fruit, right?The surprise comes when we find out this does not work for arrays. Why is that so? Because you could do the following:The main difference between List and Array here is that the List is immutable (you cannot change its contents) while the Array is mutable. As long as we are dealing with immutable types, everything is OK (as in the first example).So how does the compiler know that List is immutable? Here is the declaration of List:The +A type parameter says "List is covariant in A". That means the compilerthat there is no way to change contents of the List, which eliminates the problem we had with arrays.Simply put, a covariant class is a class from which you can read stuff out, but you can't put stuff in.Now when you already understand covariance, contravariance will be easier - it is exactly the opposite in every sense.You can put stuff in a contravariant class, but you can never get it out (imagine a- you put stuff in to be logged). That doesn't sound too useful, but there is one particularly useful application: functions. Say you've got a function taking Fruit:and filter a list of Apples using this function:So a- the filter will throw Apples in and isGoodFruit will know how to handle them.The type of isGoodFruit is actually- yes, in Scala even functions are, declared as:So functions areandOK, that's it; this is the minimal explanation I wanted to cover.