Ответ 1
Это имеет смысл для меня, во всяком случае. И это также один из самых интуитивных примеров, которые я видел: то, что слушает все события, естественно, будет слушать ключевые события или события мыши.
Я думаю о следующем примере, чтобы проиллюстрировать, почему контравариантность полезна.
Рассмотрим схему GUI с Widgets
, Events
и Event Listeners
.
abstract class Event;
class KeyEvent extends Event
class MouseEvent extends Event
trait EventListener[-E] { def listen(e:E) }
Пусть Widgets
определяют следующие методы:
def addKeyEventListener(listener:EventListener[KeyEvent])
def addMouseEventListener(listener:EventListener[MouseEvent])
Эти методы принимают только "конкретные" прослушиватели событий, и это нормально. Однако я хотел бы также определить слушателей "кухонного раковины", которые слушали все события и передавали таких слушателей методам "добавить слушателя" выше.
Например, я хотел бы определить LogEventListener
для регистрации всех входящих событий
class LogEventListener extends EventListener[Event] {
def listen(e:Event) { log(event) }
}
Так как признак EventListener
является контравариантным в Event
, мы можем передать LogEventListener
всем этим методам "add listener", не теряя при этом своей безопасности типов.
Имеет ли смысл?
Это имеет смысл для меня, во всяком случае. И это также один из самых интуитивных примеров, которые я видел: то, что слушает все события, естественно, будет слушать ключевые события или события мыши.
Имеет смысл и для меня. Как правило, параметризованный тип Type[A]
должен быть контравариантным по отношению к его параметру типа A
каждый раз, когда он должен принимать экземпляры A
, чтобы что-то делать с ними, принимая их как параметры.
Например, тип Java Comparator[T]
, если он был определен в Scala, был бы контравариантным: a Comparator[Any]
должен быть подтипом Comparator[String]
, так как он может сравнивать все объекты a Comparator[String]
можно сравнить, и многое другое. Наиболее общим примером являются типы аргументов классов FunctionX
, которые все контравариантны.