Вот контравариантный «выходной канал», который просто выводит на консоль:
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)
Поскольку контравариантность допускает это «сужение» типов (т. е. здесь от Any до String), система типов не может выставлять значения типа T, такие как это поле «истории», которое я сделал, или поле «f», которое у вас было.
Другими известными «противниками» являются функции и компараторы:
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
person
Adam Rabung
schedule
24.02.2011