scala - parameter - Ejemplo de contravarianza.
scala self class (2)
Tiene sentido para mí también. Como regla general, un tipo parametrizado tipo Type[A]
debe ser contravariante con respecto a su tipo de parámetro A
cada vez que está destinado a aceptar instancias de A
para hacer algo con ellos aceptándolos como parámetros .
Por ejemplo, el Comparator[T]
tipo Java Comparator[T]
, si se hubiera definido en Scala, habría sido contravariante: un Comparator[Any]
debería ser un subtipo de Comparator[String]
, ya que puede comparar todos los objetos un Comparator[String]
Puede comparar, y más. El ejemplo más general son los tipos de argumentos de las clases de FunctionX
, que son todas contravariantes.
Estoy pensando en el siguiente ejemplo para ilustrar por qué es útil la contravarianza.
Consideremos un marco de GUI con Widgets
, Events
y Event Listeners
.
abstract class Event;
class KeyEvent extends Event
class MouseEvent extends Event
trait EventListener[-E] { def listen(e:E) }
Deje que los Widgets
definan los siguientes métodos:
def addKeyEventListener(listener:EventListener[KeyEvent])
def addMouseEventListener(listener:EventListener[MouseEvent])
Estos métodos solo aceptan escuchas de eventos "específicos", lo cual está bien. Sin embargo, me gustaría definir también a los oyentes de "fregadero de la cocina", que escuchan todos los eventos y pasan a los oyentes a los métodos de "agregar oyente" mencionados anteriormente.
Por ejemplo, me gustaría definir LogEventListener
para registrar todos los eventos entrantes
class LogEventListener extends EventListener[Event] {
def listen(e:Event) { log(event) }
}
Dado que el rasgo EventListener
es contravariante en Event
, podemos pasar LogEventListener
a todos los métodos de "agregar oyente" sin perder su seguridad de tipo.
Tiene sentido ?
Tiene sentido para mí, de todos modos. Y también es uno de los ejemplos más intuitivos que he visto: algo que escucha todos los eventos naturalmente escuchará eventos clave o eventos del mouse.