1

观察者模式回调是 Java 中广泛使用的设计模式。然而,用匿名类实现回调接口是一件很痛苦的事情,因此在 Scala 中为这些回调类的实现引入隐式转换是很常见的。例如,Runnable 接口的隐式转换为:

implicit def func2Runnable[F](f: => F): Runnable =
  new Runnable {
    def run() {
      f
    }
  }

并假设有一些监听器注册表:

def addMyListener(m: Runnable) {
  // mock function for test
  for (i <- 1 to 2) {
    m.run()
  }
}

然后隐式转换神奇地压缩了我的代码:

addMyListener(println("Printed twice"))

问题:

当我将多行代码块传递给 addMyListener() 时,只有最后一行代码被传递给它:

addMyListener {
  println("This line is printed once")
  println("Only this is printed twice")
}

已知的解决方法:

我在转换函数上加了一个括号:

implicit def func2Runnable[F](f:() => F): Runnable =
  new Runnable {
    def run() {
      f()
    }
  }

但是当我们使用它时它很冗长:

addMyListener(() => println("Printed twice"))
addMyListener {() =>
    println("This line is printed twice")
    println("This is also printed twice")
}

有没有更清洁的解决方案来暴露这个?

4

2 回答 2

2

好的,这里有一些值得深思的地方。我添加了第二个隐式转换,但这次必须显式触发。顺便说一句,它仅与解决方法之前的部分有关。

implicit def func2Runnable2(f: => Unit) = new {
  def runnable = new Runnable {
    def run() {
      f
    }
  }
}

在 REPL 中:

scala> addMyListener ({
     |   println("This line is printed once")
     |   println("Only this is printed twice")
     | }.runnable)
This line is printed once
Only this is printed twice
This line is printed once
Only this is printed twice

所以,我的理论是第一个隐式的转换只发生在最后一行。所以它会脱糖

addMyListener ({
  println("This line is printed once")
  func2Runnable(println("Only this is printed twice"))
})

而不是预期的:

addMyListener(func2Runnable{
  println("This line is printed once")
  println("Only this is printed twice")
})

让我们检验这两个假设:

scala> addMyListener ({
     |   println("This line is printed once")
     |   func2Runnable(println("Only this is printed twice"))
     | })
This line is printed once
Only this is printed twice
Only this is printed twice

scala> addMyListener(func2Runnable{
     |   println("This line is printed once")
     |   println("Only this is printed twice")
     | })
This line is printed once
Only this is printed twice
This line is printed once
Only this is printed twice

耶!正如我想展示的那样:)

对于这个问题的更多思考,现在请注意,参数 Runnable 本身就是按名称命名的:

def addMyListener2(m: => Runnable) {
  // mock function for test
  for (i <- 1 to 2) {
    m.run()
  }
}

scala> addMyListener2 ({
     |   println("This line is printed once")
     |   println("Only this is printed twice")
     | })
This line is printed once
Only this is printed twice
This line is printed once
Only this is printed twice

推测正在发生的事情: print(...) 是 Unit,两者都是。{ prints-inside } 也是 Unit。所以它只是将 func2Runnable 的隐式转换应用于块的最后一个参数,而不是块本身。

现在看到这个的正确方法是使用scala -Xprint:namer(可能是 typer 而不是 namer)启动一个 REPL 会话。

于 2012-10-08T03:47:57.740 回答
0

自我回答:

我发现两种类型的隐式转换可以同时应用:

implicit def lazy2Runnable[F](f: => F): Runnable =
  new Runnable {
    def run() {
      f
    }
  }

implicit def func2Runnable[F](f: () => F): Runnable =
  new Runnable {
    def run() {
      f()
    }
  }

虽然在我们编写多行代码块时需要注意一点,但生成的代码还是有一点改进:

addMyListener(println("Printed twice"))
addMyListener(() => {
  println("This line is printed twice")
  println("This is also printed twice")
})
于 2012-10-09T00:57:39.907 回答