2012-12-12 1 views
2

J'implémente un système d'événements GUI dans Scala. J'ai quelque chose comme:structure de données qui contient des fermetures, paramétriquement dans scala

case class EventObject 
case class KeyEventObject extends EventObject 
case class MouseEventObject extends EventObject 

Je voudrais stocker les fermetures d'écouteur d'événements sur une carte (multi), comme suit:

var eventListeners = new MultiMap[EventDescriptor, (EventObject => Unit)]; 

Ma question est, est-il possible de réécrire cette façon que les signatures de fonction de la fermeture stockée peuvent être EventObject ou n'importe quelle sous-classe? Quelque chose comme ce qui suit:

var eventListeners = new MultiMap[EventDescriptor, [A <: EventObject](A => Unit)] 

pour que je puisse avoir le sous-type connu quand je définit les fonctions d'auditeur:

eventListeners.put(KEY_EVENT, (e:KeyEventObject) => { ... }) 
eventListeners.put(MOUSE_EVENT, (e:MouseEventObject) => { ... }) 
+1

Je dois dire que votre code est obsolète, vous devriez vérifier cette conversation: https://groups.google.com/forum/#!msg/scala-ide-dev/PyejSImLLtE/EbqqSKPUMisJ - et trouver des mots, "L'héritage d'un cas à l'autre est interdit" ... – idonnie

+0

Je ne le savais pas, j'ai standardisé sur scala 2.9 mais peut-être il est temps de mettre à niveau. –

+0

@DaveRafkind: C'est une bonne idée d'éviter l'héritage case à cas dans 2.9, aussi, où il n'est pas interdit mais entraînera des avertissements du compilateur. –

Répondre

2

Pas tant de choses sont impossibles.Vous pouvez faire ce qui suit, par exemple, avec des classes de type:

class HMultiMap { 
    import scala.collection.mutable.{ Buffer, HashMap } 

    type Mapping[K, V] 

    private[this] val underlying = new HashMap[Any, Buffer[Any]] 

    def apply[K, V](key: K)(implicit ev: Mapping[K, V]) = 
    underlying.getOrElse(key, Buffer.empty).toList.asInstanceOf[List[V]] 

    def add[K, V](key: K)(v: V)(implicit ev: Mapping[K, V]) = { 
    underlying.getOrElseUpdate(key, Buffer.empty) += v 
    this 
    } 
} 

Et maintenant:

sealed trait EventObject 
case class KeyEventObject(c: Char) extends EventObject 
case class MouseEventObject(x: Int, y: Int) extends EventObject 

sealed trait EventDescriptor 
case object KEY_EVENT extends EventDescriptor 
case object MOUSE_EVENT extends EventDescriptor 

class EventMap extends HMultiMap { 
    class Mapping[K, V] 

    object Mapping { 
    implicit object k extends Mapping[KEY_EVENT.type, KeyEventObject => Unit] 
    implicit object m extends Mapping[MOUSE_EVENT.type, MouseEventObject => Unit] 
    } 
} 

Il est un peu désordonné, mais l'utilisation est beaucoup plus jolie:

val eventListeners = new EventMap 

eventListeners.add(KEY_EVENT)((e: KeyEventObject) => println(e.c)) 
eventListeners.add(MOUSE_EVENT)((e: MouseEventObject) => println("X: " + e.x)) 
eventListeners.add(KEY_EVENT)((e: KeyEventObject) => println(e.c + " again")) 

Nous peut confirmer que nous pouvons choisir différents types de gestionnaires d'événements:

scala> eventListeners(KEY_EVENT).size 
res3: Int = 2 

Et nous pouvons faire semblant de tirer un événement et exécuter tous les gestionnaires pour elle:

scala> eventListeners(KEY_EVENT).foreach(_(KeyEventObject('a'))) 
a 
a again 

Et il est tout parfaitement sûr, puisque rien ne pénètre dans le sous-jacent carte vaguement typé sans preuves suffisantes. Nous obtiendrions une erreur de compilation si nous essayions d'ajouter une fonction de String à Unit, par exemple.

+0

merci, je pense que je l'ai maintenant. –

0

Il est impossible.

Supposons que vous avez un tel Map:

val f = eventListeners(key).head 

Comment vous appelez la fonction f? Avec le paramètre de type EventObject? Tu ne peux pas. Cela pourrait être KeyEventObject => Unit. Avec le paramètre de type KeyEventObject? Tu ne peux pas. Cela pourrait être MouseEventObject => Unit.

Vous pouvez utiliser PartialFunction[EventObject, Unit] et vérifier isDefinedAt à chaque fois, mais c'est une façon moche.

0

EDIT

semble impossible. Aussi un héritage au cas par cas s'interdit à Scala-2.10

...

Peut-être en utilisant un trait spécifique et déclarer les classes d'objets d'événements scellés et étendre ce trait?

val eventListeners = new MultiMap[EventDescriptor, ((_ >: EventTrait) => Unit)] 

De List sources:

:+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That 

That - est un type spécifique, il pourrait être construit à partir de votre EventTrait par le constructeur implicite, un constructeur pour chaque type d'événement. Au lieu de elem: B, essayez classOf [B]. Créer des méthodes get qui auront accès à votre MultiMap en utilisant différents classof:

def getMouseEvent(ed: EventDescriptor) = multimap.entrySet.filter(
(a, b) => a == ed).map((a, b) => (a, convertTo(ClassOf[MouseEvent], b)). 
filter((a, b) => b != null) 

convertTo retourne NULL si elle ne pouvait pas convertir événement type approprié

laid.

+0

Cela ne fonctionnera pas: http://stackoverflow.com/q/13828647/406435 – senia

+0

val a: Liste [((_>: Option [_]) => Unité)] = Liste ((_: ​​Int) = > {}) - n'autorise pas l'ajout de fonction depuis Int – idonnie

+0

Il n'y a aucun moyen d'appeler des fonctions d'une telle collection. Il est possible d'ajouter 'Nothing => Unit'. – senia

Questions connexes