Пример контравариантности - PullRequest
29 голосов
/ 18 марта 2011

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

Давайте рассмотрим структуру 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 всем этим методам «добавления прослушивателя», не теряя при этом безопасность типов.

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

Ответы [ 2 ]

7 голосов
/ 18 апреля 2011

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

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

7 голосов
/ 18 марта 2011

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

...