在Scala中添加和删除回调

时间:2013-12-14 21:08:23

标签: scala

这更像是一个“风格”问题......但它可能很有趣。

我写了以下特质:

trait EventProducer[B] {

  private var listeners: List[B => Unit] = List()

  def addListener(newListener: B => Unit) {
    listeners = listeners :+ newListener
  }

  protected def fireEvent(event: B) {
    listeners.map(listener => {
      listener(event)
    })
  }
}
到目前为止一切顺利。现在我想添加一个removeListener函数......但是嘿,我该怎么做?在Java中,这很简单,因为我不是使用匿名函数而是使用类,然后我可以在列表中按值找到它并将其删除。但是在这里我没有提到这个功能(除非我保留它并且它不实用)。那么实现这个的好方法是什么?

1 个答案:

答案 0 :(得分:2)

查看我的微型库Model。正如@sschaef指出的那样,你需要保留对该函数的引用。所以我从addListener(简化):

返回
class Model[U] {
  type Listener = PartialFunction[U, Unit]

  private var listeners = Vector.empty[Listener]

  def addListener(pf: Listener): pf.type = {
    listeners :+= pf
    pf
  }

  def removeListener(pf: Listener): Unit = {
    val idx = listeners.indexOf(pf)
    if (idx >=0) {
      listeners = listeners.patch(idx, Nil, 1)
    }
  }

  // ...
  def isEmpty = listeners.isEmpty
}

val m = new Model[String]
val l = m.addListener {
  case "handshake" => println("launch rockets")
}
m.removeListener(l)

要特别注意的一点是eta扩展,使用方法代替函数。

def foo(s: String) = println(s"Observed $s")

m.addListener(foo)
m.removeListener(foo)
assert(m.isEmpty)  // fails!

这最后一位实际上没有编译,因为eta扩展仅适用于函数但不适用于部分函数。在您的代码中,您使用函数B => Unit,因此请注意这个陷阱。