Здравствуйте, eao197, Вы писали:
Собственно, с этим все понятно и у меня никогда проблем с ковариантностью не было. А вот контравариантность -- она вроде как есть, даже в стандартной библиотеке Scala используется:
E>E>abstract trait Function1 [-T1, +R] extends java.lang.Object with scala.ScalaObject
E>
E>но, блин, сходу вспомнить, что она означает и придумать какой-то пример ее использования -- не получается.
Что такое Function1, довольно просто объяснить.
Это, насколько я понял, функция с одним аргументом, запакованная в объект; в аннотации вариантности [-T1, +R] закодированы обычные правила подтипизации функций. Над ними нужно пару раз помедитировать, но на самом деле ничего сложного нет: тип функций a' -> b' будет подтипом a -> b тогда, когда b' является подтипом b и a является подтипом a'. Почему так? Допустим, мы передаем функцию как аргумент (псевдокод):
class Animal : Object {}
class Banana : Plant {}
class Apple : Plant {}
class Elephant : Animal {}
function foo(f : Plant->Animal)
{
Animal animal = f(new Banana);
}
function ok(x : Fruit) : Elephant
{
...
return new Elephant;
}
function bad1(x : Apple) : Animal
{
...
}
function bad2(x : Plant) : Object
{
...
}
...
foo(ok); // т.к. банан - тоже фрукт, ok умеет с ним работать.
// т.к. слон - тоже животное, foo сможет обработать возвращенное значение
foo(bad1); // bad1 не справится с бананом, она умеет есть только яблоки
foo(bad2); // foo не поймет, что ей вернула функция bad2,
// она ожидала животное, а получила непонятный "объект"
Более формально об этом можно почитать в статьях Луки Карделли, например:
On understanding types, data abstraction, and polymorphism
Typeful programming.