隐式转换为Runnable?

18
作为一项练习,我尝试创建一个隐式转换,它将接受一个函数并生成一个Runnable对象。这样,您就可以调用接受Runnable对象的Java方法,并像使用闭包一样使用它们。
隐式转换是足够简单的:
    implicit def funToRunnable(fun : Unit) = new Runnable() { def run = fun }

但我不知道该如何调用它。如何传递一个返回Unit的无参数函数,而不让它立即被求值?例如,我希望以下代码输出"12",但实际上它输出了"21",因为print("2")被立即求值了。

    var savedFun : Runnable = null
    def save(r : Runnable) = { savedFun = r }

    save(print("2"))
    print("1")
    savedFun.run()

我如何告诉编译器将print("2")视为函数体,而不是立即执行的内容?我尝试了一些可能性,比如:

    save(() => print("2"))
或者
    save(=> print("2"))

不是合法的语法。


你的意思是“调用接受Runnables的Java方法,并像闭包一样将函数传递给它们”吗?因为接受其他函数的方法并不被称为闭包;它们所接受的函数有时被称为闭包。 - Erik Kaplun
5个回答

24

糟糕,我自己回答了我的问题。我错误地实现了隐式转换。正确的实现方式是:

implicit def funToRunnable(fun: () => Unit) = new Runnable() { def run() = fun() }

然后你可以这样调用它:

save(() => print("2"))
这将产生 "2"。

我也在同一条路上。你有没有想法为什么 def run = run 不起作用? - OscarRyz
2
def run = run 会一直产生无限递归。即使在封闭作用域中定义了 run,这个 def 定义的会覆盖它,确保直接、无条件地递归调用。 - Randall Schulz
我的意思是 def run = fun 没有括号。 - OscarRyz
2
@Support 是因为没有括号的 fun 只是创建了一个返回传递的函数的方法。要调用函数类型,必须使用括号,否则你只是返回函数本身。 - Ken Bloom
5
它不应该输出2而不是12吗? - David Frank

14
如果你想冒险,你可以将任何东西转换为可运行的代码:
implicit def whateverToRunnable[F](f: => F) = new Runnable() { def run() { f } }

scala> val t = new Thread(println("Hello"))
t: java.lang.Thread = Thread[Thread-2,5,main]

scala> t.start()
Hello

或者你可以创建自己的线程创建器和启动器:

def thread[F](f: => F) = (new Thread( new Runnable() { def run() { f } } )).start

scala> thread { println("Hi"); Thread.sleep(1000); println("Still here!") }
Hi

scala> Still here!

如果您想返回线程,则
def thread[F](f: => F) = {
  val t = new Thread( new Runnable() { def run() { f } } )
  t.start()
  t
}

但是,所有这些虽然有用,但也许比scala.actors.Futures(仅在2.8上测试)还不如有用:

scala> import scala.actors.Futures

scala> val x = Futures.future { Thread.sleep(10000); "Done!" }
x: scala.actors.Future[java.lang.String] = <function0>

scala> x.isSet
res0: Boolean = false

scala> x.isSet
res1: Boolean = false

scala> x()   // Waits until the result is ready....
res2: java.lang.String = Done!

5
实际上,您甚至可以使用按名称调用参数更好地完成此操作:
implicit def runnable(f: => Unit): Runnable = new Runnable() { def run() = f }

使用方法:

import concurrent.ExecutionContext.Implicits.global._
execute(print("hello"))

5
有趣的是,通过这种方式,您可以执行接收Runnable并将其传递给闭包的代码。

参见:

scala> new Thread( ()  => print( "Hello" ) ).start()
<console>:5: error: overloaded method constructor Thread with alternatives (java.lang.ThreadGroup,java.lang.Runnable,java.lang.String,Long)java.lang.Thread <and> (java.lang.ThreadGroup,java.lang.Runnable,java.lang.String)java.lang.Thread <and> (java.lang.Runnable,java.lang.String)java.lang.Thread <and> (java.lang.ThreadGroup,java.lang.String)java.lang.Thread <and> (java.lang.String)ja...
       new Thread( ()  => print( "Hello" ) ).start()


scala> implicit def funcToRunnable( func : () => Unit ) = new Runnable(){ def run() = func() }
funcToRunnable: (() => Unit)java.lang.Object with java.lang.Runnable

scala> def doRun( runnable: Runnable ) = runnable.run
doRun: (Runnable)Unit

scala> doRun( () => print("Hola"))
Hola

scala> new Thread(()=>print("Hello")).start()

scala> Hello

0

在不同的线程中运行一些代码的另一种方法:

scala.actors.Actor.actor { ...doSomething()... }

Runnable 不仅仅用于在新线程中运行任务。例如,在 GUI 开发中,它被用于在特殊的 UI 线程上运行任务。 - Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接