2

私は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) => { ... })
4

3 に答える 3

2

不可能なことはそれほど多くありません。たとえば、型クラスを使用して次のことを実行できます。

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

于 2012-12-13T02:04:02.963 に答える
0

それは不可能だ。

あなたがそのようなものを持っているとしましょうMap:

val f = eventListeners(key).head

関数をどのように呼び出しますfか? 型のパラメータでEventObject? できません。かもしれませんKeyEventObject => Unit。型のパラメータでKeyEventObject? できません。かもしれませんMouseEventObject => Unit

毎回forを使用PartialFunction[EventObject, Unit]してチェックできますが、それは醜い方法です。isDefinedAt

于 2012-12-12T22:06:32.680 に答える
0

編集

無理そうです。また、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- 特定のタイプです。イベント タイプごとに 1 つのビルダーである暗黙的なビルダーによって EventTrait から構築できます。代わりに、elem: BclassOf[B] を使用してください。MultiMap別の classOf を使用してアクセスする 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 を返します

醜い。

于 2012-12-12T22:06:49.047 に答える