
Copyright © Cay S. Horstmann 2015 
This work is licensed under a Creative Commons Attribution 4.0 International License
Given classes
class Pair[T](val first: T, val second: T) class Person(val name: String) class Student(name: String, val major: String) extends Person(name)and a method
def makeFriends(p: Pair[Person]): Unit = { ... }
which of the following can be passed to makeFriends?
Pair[Person]Pair[Person] and a Pair[Student]Pair[Person] and a Pair[Any]Pair[...]G is a generic type, there is no relationship between G[S] and G[T], no matter what the relationship between S and T may be.class Pair[+T](val first: T, val second: T)
Pair[Student] <: Pair[Person]Seq[+A])A Friend[T] is willing to befriend anyone of type T.
trait Friend[T] {
def befriend(someone: T)
}
Suppose you have a function
def makeFriendWith(s: Student, f: Friend[Student]): Unit = { f.befriend(s) }
What should you be able to pass in for f?
Friend[Student]Friend[Person] and a Friend[Student]Friend[GraduateStudent] and a Friend[Student]Friend[...]G is contravariant if
S <: T => G[T] <: G[S]
Student <: Person => Friend[Person] <: Friend[Student]
- annotation:
trait Friend[-T] {
def befriend(someone: T)
}trait Function1[-T, +R]
def friends(students: Vector[Student], find: Function1[Student, Person])with
def findStudent(p: Person) : Student
Student objectsStudent objects, which are Person objectsArray[Student] to an Array[Person]?
val students = Array(student1, student2)
val people = students // Not legal, but suppose it was...
people(0) = new Person("Fred")
Now students(0) is a Person, not a Student!class Pair[+T](var first: T, var second: T)
T occurs in contravariant position in first_=(value: T)
”foldLeft[B](z: B)(op: (A, B) => B): B
- + + - +
A can be +, but B must be invariantclass Pair[+T](val first: T, val second: T) {
def replaceFirst(newFirst: T) = new Pair[T](newFirst, second)
}
newFirst: T is in contravariant positiondef replaceFirst[R >: T](newFirst: R) = new Pair[R](newFirst, second)
R is invariant and can occur in a covariant positionabstract class List[+T] case class ::[T](val head: T, tail: List[T]) extends List[T] case object Nil extends List[Nothing]
Nothing is a subtype of all typesList[Nothing] is a subtype of all List[T]val lst = new ::(42, Nil)
::[Int]. List[Int]List[Nothing] <: List[Int]? extends T, ? super Tpublic class Collections { // This is Java
public static <T> void copy(List<? super T> dest, List<? extends T> src) { ... }
...
}Comparator as contravariant—must provide variance with every use
public class Collections { // This is Java
public static <T> void sort(List<T> list, Comparator<? super T>> c) { ... }
...
} def copy[T](dest: Array[_ >: T], src: Array[_ <: T])

count, groupBy, and aggregate methods of the Iterable[+A] trait. Why is it in a covariant position in these methods?
replaceFirst method has a type bound. Why can’t you define an equivalent
method on a mutable Pair[T]?
def replaceFirst[R >: T](newFirst: R) { first = newFirst } // Error
replaceFirst method for an immutable Pair[+T]. But there is a reason. Suppose you could define
def replaceFirst(newFirst: T) = new Pair(newFirst, second)Then you could also define
class NastyDoublePair(x: Double, y: Double) extends Pair[Double](x, y) {
override def replaceFirst(newFirst: Double)
= new Pair(math.sqrt(newFirst), second)
}
Now what would happen with this call?
val p: Pair[Any] = new NastyDoublePair(3, 4)
val q = p.replaceFirst("Hello")
Is the first line valid? That is, can you assign a NastyDoublePair to a Pair[Any]? Pair[Any].replaceFirst with a "String" (assuming that it is defined as above?)class MyMap[K, V](entries: List[(K, V)]) {
def this() { this(Nil) }
def get(key: K) = entries.find(_._1 == key) match {
case Some(e) => Some(e._2)
case None => None
}
def put(key: K, value: V) = new MyMap((key, value) :: entries)
}
Make an instance as
val mm = new MyMap[String, Int].put("Fred", 1).put("Wilma", 10).put("Fred", 5)
What do you get for mm.get("Fred")
mm.get("Barney")def total(students: List[Student], scores: MyMap[Student, Int]) = students.map(scores.get(_).getOrElse(0)).sumShould it be ok to pass it a
MyMap[Person, Int]?MyMap to make that work. What did you do?def findById(ids: List[Int], people: MyMap[Int, Person]) = ids.flatMap(people.get(_))Should it be ok to pass it a
MyMap[Int, Student]?scala.collection.immutable.Map[A, +B] contravariant in A?class Person(val name: String) extends Ordered[Person] {
def compare(that: Person) = name.compareTo(that.name)
}
class Student(name: String, val major: String) extends Person(name)
Make a Person and a Student object. Can you call person.compare(student)? student.compare(person)?Vector[Person], fill it with some people, and call people.max. What happens.Vector[Student]. What happens?Ordered in Scaladoc.)Ordered to be a generic trait with a single abstract method compare(that: T) : Int. Make it contravariant so that a Ordered[Person] is also a Ordered[Student]. Define max[T <: Ordered[T]](s: Seq[T]) = s.reduce(...). What is max?max(people)? max(students)? Ordered contravariant? It's been discussed at great length here. Just have a glance at it and admire the complexity and the emotions :-)Ordered, or make T invariant. The call max(students) should no longer compile. In Java, you'd do this:
<T extends Comparable<? super T>> max(Collection<T> coll)or in Scala notation
def max[T <: Ordered[_ >: T]]Why does this solve the problem?
type SuperOrdered[T] = Ordered[_ >: T] def max[T <: SuperOrdered[T]]Now what happens?
Do this as individual work, not with your partner
When all done, email the signed zip files to Fatemeh.Borran@heig-vd.ch