scala covariance and contravariance
trait Function1[-P, +R] { ... }
prameter is contravariance and result is covariance
/**
* Created by Rosin on 2014/7/3.
*/
object Main20140703 extends App{
val doAVary: Function1[MyGrandChild, GrandChildReturn] = { x: MyChild => new MyGrandChild }
//doAVary(new MyGrandGrandGrandChild)
}
class Me{}
class MyChild extends Me{}
class MyGrandChild extends MyChild
class MyGrandGrandChild extends MyGrandChild
class MyGrandGrandGrandChild extends MyGrandGrandChild
class AReturn
class ChildReturn extends AReturn{}
class GrandChildReturn extends ChildReturn{}
class GrandGrandChildReturn extends GrandChildReturn{}
class GrandGrandGrandChildReturn extends GrandGrandChildReturn{}
mutable collections are invarian(neither covariance nor contravariance)
A type parameter is invariant when it’s neither covariant nor contravariant. All Scala mutable collection classes are invariant.
An example can explain why mutable objects need to be invariant. By now, you should be comfortable using collection .immutable.List;
the mutable counterpart of List is collection.mutable.ListBuffer. Because ListBuffer is mutable, it’s declared as invariant as follows:
final class ListBuffer[A] ...{ ... }
Notice that when you declare an invariant type, you drop the - or + sign. Because it’s
declared as invariant, you can’t assign ListBuffer from one type to another. The
following code will throw a compilation error:
scala> val mxs: ListBuffer[String] = ListBuffer("pants")
mxs: scala.collection.mutable.ListBuffer[String] = ListBuffer(pants)
scala> val everything: ListBuffer[Any] = mxs
<console>:6: error: type mismatch;
found : scala.collection.mutable.ListBuffer[String]
required: scala.collection.mutable.ListBuffer[Any]
val everything: ListBuffer[Any] = mxs
Even though String is a subtype of scala.Any, Scala still doesn’t let you assign mxs
to everything. To understand why, assume ListBuffer is covariant and the following
code snippet works without any compilation problem:
scala> val mxs: ListBuffer[String] = ListBuffer("pants")
mxs: scala.collection.mutable.ListBuffer[String] = ListBuffer(pants)
scala> val everything: ListBuffer[Any] = mxs
scala> everything += 1
res4: everything.type = ListBuffer(1, pants)
Can you spot the problem? Because everything is of the type Any, you can store an integer value into a collection of strings. This is a disaster waiting to happen.
It’s exactly what happens to Java arrays. To avoid these kinds of problems, it’s always a good idea to make mutable objects invariant.
The next question is what happens in case of an immutable object for collections. It turns out that for immutable objects,
covariance isn’t a problem at all. If you replace ListBuffer with the immutable List, you can take an instance of List[String] and assign it to List[Any] without a problem.
because mxs is a mutable LisBuffer,even though mxs has been assigned to the everything which is of type ListBuffer[String] ,but actually,mxs's real type is [String]
what if a morphysm method invoke on everything invoke ..... actually invoke on String.
scala> val xs: List[String] = List("pants")
xs: List[String] = List(pants)
scala> val everything: List[Any] = xs
everything: List[Any] = List(pants)
The only reason this assignment is safe is because List is immutable. You can add 1 to xs List, and it will return a new List of type Any.
scala> 1 :: xs
res5: List[Any] = List(1, pants)
now you can not make a morphysm method invoke on res5.because it's actual type is really Any.
Again, this addition is safe because the cons(::) method always returns a new List, and its type is determined by the type of elements in the List. The only type that could store an integer value and reference value is scala.Any.
This is an important property to remember about type variance when dealing with mutable/ immutable objects.