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



我正在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)]

这样我就可以在定义侦听器函数时知道子类型:

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

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

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 ,我们会得到一个编译时错误。

编辑

似乎不可能。此外,Scala-2.10 中也禁止逐案继承

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

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

来自List来源:

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

That - 是一种特定类型,它可以通过隐式构建器从您的 EventTrait 构建,每个事件类型一个构建器。与其elem: B尝试使用classOf[B],不如尝试使用classOf[B]。创建将使用不同的类访问MultiMap的 get 方法:

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

丑。

是不可能的。

假设你有这样的Map

val f = eventListeners(key).head

您将如何调用该函数f?参数类型为 EventObject ?你不能。可能是KeyEventObject => Unit.参数类型为 KeyEventObject ?你不能。可能是MouseEventObject => Unit.

你可以使用PartialFunction[EventObject, Unit],每次都检查isDefinedAt,但这是一种丑陋的方式。

最新更新