Capture InterruptedException em Scala com try {] catch {}, não scala.util.Experimente

Digamos que você tenha um método que executará como um thread:

def functionAsThread[F](function: => F, name: Option[String] = None) = {
val t
= new Thread( new Runnable() { def run() { function } } )
t
.start()
name
.foreach(t.setName)
t

}

def periodicallyDoSomething(): Unit = {
while(keepDoingIt_?) {
doSomething
()
Thread.sleep(frequency)
}
}

val somethingDoer
= functionAsThread(periodicallyDoSomething, Some("SomethingDoer"))

Quando, inevitavelmente, o thread deve parar, como em um desligamento limpo, é claro que você dirá somethingDoerpara parar com somethingDoer.interrupt().

Como você deve lidar com o potencial InterruptedExceptiongerado Thread.sleep?

Seu cérebro Scala deve levá-lo para embrulhar functionem um Trye passar em um PartialFunctionpara a recuperação, assim:

def functionAsThread[F](function: => F, name: Option[String] = None, recoverWith: PartialFunction[Throwable, Unit]) = {
val t
= new Thread( new Runnable() { def run() { Try(function).recover(recoverWith) } } )
t
.start()
name
.foreach(t.setName)
t

}

Você ficará tão frustrado quanto eu quando isso não funcionar. A exceção ainda não foi capturada!

Analisando Tryum pouco mais, vemos por que isso não funciona:

object Try {
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}

Isso é interessante. Tryé apenas um que retorna um objeto ou conveniente . Mas que negócio é esse ?try{}catch{}SuccessFailureNonFatal

object NonFatal {
def apply(t: Throwable): Boolean = t match {
// VirtualMachineError includes OutOfMemoryError and other fatal errors
case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
case _ => true
}
def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None

Scala considera InterruptedExceptionser uma exceção fatal. Ou seja, há uma decisão de design deliberada que um implementador deve usar para lidar com isso. Isso faz sentido porque um thread que está inativo deve ser informado explicitamente sobre o que fazer se for interrompido. Isso é apenas uma parte do uso .try{}catch{}Thread.sleep

Portanto, uma maneira de corrigir o método é:

def periodicallyDoSomething(): Unit = {
try {
while(keepDoingIt_?) {
doSomething
()
Thread.sleep(frequency)
}
} catch {
case e: InterruptedException => handleInterruption()
}

Pode ser melhor apenas experimentar , mas deixarei essa melhoria como um exercício para o leitor!Thread.sleep