Вот противоположный «выходной канал», который просто печатает на консоль:
class OutputChannel[-T] {
def write(t:T) = println(t);
}
Вот он в действии:
val out:OutputChannel[Any] = new OutputChannel[Any]
out.write(5)
Ничего интересного пока нет.Крутая вещь в контрасте - теперь вы можете безопасно назначить этот выходной канал тому, который принимает любой подкласс T:
val out2:OutputChannel[String] = out
out2.write("five")
out2.write(55) //wont compile
Теперь, представьте, если мы добавили отслеживание истории в выходной канал - чтобы вернутьСписок вещей, которые были отправлены до сих пор.
//!!! as you've seen code like this won't compile w/ contravariant types!!!!
class OutputChannel[-T] {
var history:List[T] = Nil
def write(t:T) = {
history = history :+ t;
println(t);
}
}
Если вышеприведенное скомпилируется, у пользователя выходного канала на основе строк возникнет проблема:
//history(0) is an Int - runtime exception (if scala allowed it to compile)
val firstStringOutputted:String = out2.history(0)
Поскольку контравариантность допускает это «сужение» типов (т.е. изЛюбое для String здесь), система типов не может отображать значения типа T, такие как это поле «history», которое я сделал, или поле «f», которое вы имели.
Другими известными «противоположностями» являются Function и Comparators:
val strHashCode:String => Int = { s:Any => s.hashCode } //function which works with any object
val strComp:Comparator<String> = new HashCodeComparator() //comparator object which works with any object