在scala中参数化地保存闭包的数据结构

在scala中参数化地保存闭包的数据结构,scala,types,closures,bounds,parametric-polymorphism,Scala,Types,Closures,Bounds,Parametric Polymorphism,我正在Scala中实现一个GUI事件系统。我有点像: case class EventObject case class KeyEventObject extends EventObject case class MouseEventObject extends EventObject 我想将事件侦听器闭包存储在(多)映射中,如下所示: var eventListeners = new MultiMap[EventDescriptor, (EventObject => Unit)];

我正在Scala中实现一个GUI事件系统。我有点像:

case class EventObject
case class KeyEventObject extends EventObject
case class MouseEventObject extends EventObject
我想将事件侦听器闭包存储在(多)映射中,如下所示:

var eventListeners = new MultiMap[EventDescriptor, (EventObject => Unit)];
我的问题是,有没有办法重写它,以便存储闭包的函数签名可以是EventObject或任何子类?如下所示:

var eventListeners = new MultiMap[EventDescriptor, [A <: EventObject](A => Unit)]
这是不可能的

假设您有这样的
映射

val f = eventListeners(key).head
如何调用函数
f
?参数类型为
EventObject
?你不能。它可以是
KeyEventObject=>Unit
。参数类型为
KeyEventObject
?你不能。它可以是
MouseEventObject=>Unit

您可以使用
PartialFunction[EventObject,Unit]
并每次检查
是否已定义,但这是一种丑陋的方式。

编辑

似乎不可能。在Scala-2.10中还禁止案例间继承

也许通过使用某些特定的特性并声明事件对象类来密封和扩展该特性

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

:+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That
-是一种特定类型,它可以由隐式构建器根据EventTrait构建,每个事件类型一个构建器。尝试使用classOf[B]代替
elem:B
。创建get方法,该方法将使用以下不同类别访问您的
MultiMap

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
如果无法将事件转换为适当的类型,则返回null


丑陋。

并不是说很多事情是不可能的。例如,可以使用类型类执行以下操作:

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
  }
}
现在:

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]
  }
}
它有点凌乱,但用法要漂亮得多:

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"))
我们可以确认,我们可以选择各种事件处理程序:

scala> eventListeners(KEY_EVENT).size
res3: Int = 2
我们可以假装触发一个事件并运行它的所有处理程序:

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

这一切都是完全安全的,因为没有适当的证据,任何东西都不会进入底层的松散类型映射。例如,如果我们试图将函数从
String
添加到
Unit
,我们会遇到编译时错误。

val a:List[((()。可以添加
Nothing=>Unit
。他不会添加任何内容或AnyRef,一切都会好起来。我必须说,您的一些代码已弃用,您应该检查此对话:-并找到单词,“禁止案例间继承”…我不知道,我在scala 2.9上进行了标准化,但也许是时候升级了。@DaveRafkind:在2.9中避免大小写继承也是一个好主意,因为它不被禁止,但会导致编译器警告。
scala> eventListeners(KEY_EVENT).foreach(_(KeyEventObject('a')))
a
a again