2010-06-18 3 views
17

В качестве упражнения я попытался создать неявное преобразование, которое примет функцию и создаст Runnable. Таким образом, вы можете вызвать методы Java, которые принимают объекты Runnable, и использовать их как закрытие.Неявное преобразование в Runnable?

Неявное достаточно легко:

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

Однако я не знаю, как это назвать. Как вы передаете функцию no-arg, которая возвращает 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")) 

не являются законным синтаксисом.

+0

вы имели в виду * "методы Java вызовов, которые принимают Runna bles и pass функции в них, как закрытие "*? потому что методы, которые принимают другие функции, не называются замыканиями; функции, которые они принимают, называются (иногда) замыканиями. –

ответ

23

arg, просто ответил на мой вопрос. Я неправильно применил неявное преобразование. Правильная реализация

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

и вы называете это так:

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

Это дает «12»

+0

Я был на том же треке. Вы знаете, почему 'def run = run' не работает? – OscarRyz

+2

'def run = run' всегда будет бесконечной рекурсией. Даже если в охватывающей области есть пробел, тот, который определяется этим 'def', будет затенять его, гарантируя прямой, безусловный рекурсивный вызов. –

+0

Я имел в виду 'def run = fun' без parens – OscarRyz

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 
12

Если вы хотите жить опасно, вы можете преобразовать что-нибудь к исполняемым:

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! 
0

Еще один способ запустить код в другом потоке:

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

Runnable используется не только для запуска материалов в новых потоках. Например, в графическом интерфейсе он используется для запуска файлов в специальном потоке пользовательского интерфейса. – Martin

4

На самом деле, вы можете сделать это даже приятнее с вызова по имени аргумента:

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

Использование:

import concurrent.ExecutionContext.Implicits.global._ 
execute(print("hello")) 
Смежные вопросы