В Scala есть функция, позволяющая определить класс, который будет ковариантным / контравариантным в его общих параметрах.
В качестве примера ковариации: естественно думать, что если class Student extends Person
, то List[Student]
"расширяет" List[Person]
. Это связано с тем, что у каждого метода, принимающего List[Person]
, не должно возникнуть проблем при работе с объектом List[Student]
. Это невозможно в Java (не делая метод также общим).
Contravariance противоположен и немного сложнее объяснить. Требуется, когда тип должен быть передан в общий класс, а не прочитан (в List[Person]
вы читаете элементы списка). Общий пример - это функция. В него помещаются типы аргументов функции, поэтому, если метод ожидает функцию Person => String
, он не может быть вызван с функцией Student => String
(он вызовет аргумент с человеком, но ожидает студента )
Scala также определяет Nothing
, чтобы неявно расширять все. Это нижний тип. Так что List[Nothing]
всегда «расширяет» List[X]
для любого X. List()
создает List[Nothing]
, и ковариация - вот почему вы можете написать val x: List[Person] = List()
.
В любом случае, Карта инвариантна в своих типах ключей. Причина в том, что Map[A, B]
подобна функции A => B
, поэтому она может быть только контравариантной в A
. Другой способ - подумать, что произойдет, если вы передадите Map[Student, String]
методу, ожидающему Map[Person, String]
, очевидно, он может попытаться поместить туда объекты Person
, что нехорошо, другой способ в порядке. С другой стороны, карту можно рассматривать как Iterable[(A, B)]
, здесь она должна быть ковариантной в A. Таким образом, она инвариантна по своему значению.
В результате вы не можете присвоить Map[Nothing, Nothing]
переменной типа Map[String, Animal]
. Map()
создает Map[Nothing, Nothing]
Компилятор скажет вам следующее:
scala> val dogs3 = Map3[Dog]()
<console>:13: error: type mismatch;
found : scala.collection.immutable.Map[Nothing,Nothing]
required: Map[String,Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
val dogs3 = Map3[Dog]()
^