2011-03-18 6 views
28

Estoy pensando en el siguiente ejemplo para ilustrar por qué la contravarianza es útil.Ejemplo de contravariancia

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 Widgets definen los métodos siguientes:

def addKeyEventListener(listener:EventListener[KeyEvent]) 
def addMouseEventListener(listener:EventListener[MouseEvent]) 

Estos métodos sólo aceptan los detectores de eventos "específicas", lo cual está bien. Sin embargo, me gustaría definir también oyentes de "fregadero de cocina" que escuchen todos los eventos y pasen a dichos oyentes a los métodos de "agregar oyente" más arriba.

Por ejemplo, me gustaría definir LogEventListener a registrar todos los eventos entrantes

class LogEventListener extends EventListener[Event] { 
    def listen(e:Event) { log(event) } 
}

Desde el rasgo EventListener es contravariant en Event podemos pasar LogEventListener a todos esos métodos "añadir oyente" sin perder su tipo de seguridad.

¿Tiene sentido?

+0

+1 buena pregunta, mala tasa de aceptación. ¡triste! – Nishant

Respuesta

6

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 de forma natural escuchará eventos clave o eventos de mouse.

6

Tiene sentido para mí también. Como regla general, un tipo parametrizado Type[A] debe ser contravariante con respecto a su parámetro de tipo Acada vez que se pretende aceptar instancias de A para hacer algo con ellos al aceptarlos como parámetros.

Por ejemplo, el tipo de Java Comparator[T], si se hubiera definido en Scala, habría sido contravariant: un Comparator[Any] debe ser un subtipo de Comparator[String], ya que puede comparar todos los objetos de una Comparator[String] se puede comparar, y más. El ejemplo más general es el tipo de argumento de las clases FunctionX, que son todas contravariantes.