Подтвердить что ты не робот

Пример контравариантности

Я думаю о следующем примере, чтобы проиллюстрировать, почему контравариантность полезна.

Рассмотрим схему 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", не теряя при этом своей безопасности типов.

Имеет ли смысл?

4b9b3361

Ответ 1

Это имеет смысл для меня, во всяком случае. И это также один из самых интуитивных примеров, которые я видел: то, что слушает все события, естественно, будет слушать ключевые события или события мыши.

Ответ 2

Имеет смысл и для меня. Как правило, параметризованный тип Type[A] должен быть контравариантным по отношению к его параметру типа A каждый раз, когда он должен принимать экземпляры A, чтобы что-то делать с ними, принимая их как параметры.

Например, тип Java Comparator[T], если он был определен в Scala, был бы контравариантным: a Comparator[Any] должен быть подтипом Comparator[String], так как он может сравнивать все объекты a Comparator[String] можно сравнить, и многое другое. Наиболее общим примером являются типы аргументов классов FunctionX, которые все контравариантны.