Неявное преобразование в Runnable?

17

В качестве упражнения я попытался создать неявное преобразование, которое примет функцию и создаст Runnable . Таким образом, вы можете вызвать методы Java, которые принимают 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"))

не являются юридическим синтаксисом.

    
задан Patrick Arnesen 19.06.2010 в 01:28
источник
  • вы имели в виду «вызовы java-методов, которые принимают Runnables и пропускают в них функции как закрытие»? потому что методы, которые принимают другие функции, не называются замыканиями; функции, которые они принимают, называются (иногда) замыканиями. –  Erik Allik 31.03.2014 в 04:38

5 ответов

23

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

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

, и вы вызываете это следующим образом:

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

Это дает «12»

    
ответ дан Patrick Arnesen 19.06.2010 в 01:40
  • Я был на том же треке. Вы знаете, почему def run = run не работает? –  OscarRyz 19.06.2010 в 01:49
  • def run = run всегда будет бесконечной рекурсией. Даже если есть пробег, определенный в охватывающей области, тот, который определяется этим def, будет затенять его, гарантируя прямой, безусловный рекурсивный вызов. –  Randall Schulz 19.06.2010 в 03:27
  • Я имел в виду def run = fun without parens –  OscarRyz 20.06.2010 в 07:00
  • @Поддержка, потому что удовольствие без круглых скобок просто создает метод, который возвращает переданную функцию. Чтобы вызвать тип функции, вы должны использовать круглые скобки, иначе вы просто возвращаете функционал. –  Ken Bloom 22.06.2010 в 16:03
  • Должен ли он давать 2 вместо 12? –  David Frank 04.09.2013 в 10:05
12

Если вы хотите жить опасно, вы можете преобразовать что угодно в 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

Или вы могли бы создать свой собственный создатель потока и стартер:

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!
    
ответ дан Rex Kerr 19.06.2010 в 04:09
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
    
ответ дан OscarRyz 19.06.2010 в 01:54
4

Собственно, вы можете сделать это еще лучше с аргументом по имени:

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

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

import concurrent.ExecutionContext.Implicits.global._
execute(print("hello"))
    
ответ дан vmolchanov 20.04.2015 в 23:30
0

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

scala.actors.Actor.actor { ...doSomething()... }
    
ответ дан Jona Christopher Sahnwaldt 03.05.2012 в 13:54
  • Runnable используется не только для запуска файлов в новых потоках. Например, в графическом интерфейсе он используется для запуска файлов в специальном потоке пользовательского интерфейса. –  Martin 24.05.2012 в 11:09