2010-06-18 14 views
17

W ramach ćwiczenia próbowałem utworzyć niejawną konwersję, która zaakceptowałaby funkcję i wygenerowała Runnable. W ten sposób możesz wywoływać metody Java, które akceptują obiekty i używać ich jako zamknięć.Implicit conversion to Runnable?

Domniemana konwersja jest dość prosta:

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

Jednak nie wiem jak to nazwać. W jaki sposób przekazujesz funkcję no-arg, która zwraca Unit, bez konieczności jednoczesnej oceny? Na przykład, chciałbym, aby poniższe wydrukowały "12", ale zamiast tego wypisują "21", ponieważ print("2") jest oceniany jednocześnie.

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

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

Jak mogę poinformować kompilator traktować print("2") jako ciało funkcji, a nie coś, aby być oceniane na raz? Niektóre możliwości próbowałem, takie jak

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

lub

save(=> print("2")) 

nie są składnia prawny.

+0

chodziło * "metody java połączeń, które akceptują Runna przepuszcza i przekazuje im funkcje takie jak zamknięcia "*? ponieważ metody, które akceptują inne funkcje, nie są nazywane zamknięciami; funkcje, które akceptują, są nazywane (czasami) zamknięciami. –

Odpowiedz

23

arg, właśnie odpowiedziałem na moje własne pytanie. Zaimplementowałem niejawną konwersję niepoprawnie. Prawidłowa realizacja jest

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

i nazwać go tak:

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

Daje to „12”

+0

Byłem na tym samym torze. Czy masz pojęcie, dlaczego 'def run = run' nie działa? – OscarRyz

+2

'def run = run' zawsze będzie nieskończoną rekurencją. Nawet jeśli istnieje 'run' zdefiniowany w zasięgu obejmującym, zdefiniowany przez' def' będzie go śledzić, gwarantując bezpośrednie, bezwarunkowe wywołanie rekurencyjne. –

+0

Mam na myśli 'def run = fun' bez parens – OscarRyz

5

ciekawe, w ten sposób można wykonać kod, który odbiera Runnable i przekazać je do zamknięcie.

Patrz:

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

Jeśli chciał żyć niebezpiecznie, można konwertować cokolwiek Do runnable:

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 

Albo można stworzyć swój własny wątek-creator-and-rozrusznik:

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! 

Jeśli chcesz zwrócić wątek, to

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

Ale wszystko to, choć przydatne, jest być może jeszcze mniej przydatne niż scala.actors.Futures (testowane tylko na 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

Jeszcze innym sposobem, aby uruchomić jakiś kod w innym wątku:

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

Runnable służy nie tylko do uruchamiania rzeczy w nowo utworzonych wątkach. Na przykład w rozwoju GUI służy do uruchamiania rzeczy w specjalnym wątku UI. – Martin

4

Właściwie można to zrobić nawet ładniejszy z argumentów wywołania przez nazwy:

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

Zastosowanie:

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