Инициализация vals, которая может вызвать исключение

Мне нужно инициализировать набор vals, где код для инициализации может вызвать исключение. Я хотел бы написать:

try {
  val x = ... generate x value ...
  val y = ... generate y value ...
} catch { ... exception handling ... }

... use x and y ...

Но это (очевидно) не работает, потому что x и y не входят в объем вне try.

Легко решить проблему с помощью изменяемых переменных:

var x: Whatever = _
var y: Whatever = _
try {
  x = ... generate x value ...
  y = ... generate y value ...
} catch { ... exception handling ... }

... use x and y ...

Но это не совсем так приятно.

Также легко решить проблему путем дублирования обработки исключений:

val x = try { ... generate x value ... } catch { ... exception handling ... }
val y = try { ... generate y value ... } catch { ... exception handling ... }

... use x and y ...

Но это связано с дублированием обработки исключений.

Должен быть "хороший" способ, но он ускользает от меня.

Ответ 1

Простое решение было бы определить функцию-обертку, которая использует параметры по-имени.

def safely[T](f: => T): T = try { f } catch { /* exception handling */ }
// You'll have to play around with safely type signature, depending on what
// you do in the exception handling
val x = safely { generateX }
val y = safely { generateY }

Или, если вы чувствуете себя очень шикарно, Scala 2.9 позволяет частичную функцию использовать в качестве обработчика исключений.

val handler: PartialFunction[Throwable, Unit] = { /* exception handling */ }
val x = try { generateX } catch handler
val y = try { generateY } catch handler

В целом, я бы сказал, что первый метод является самым простым. Но если вы можете написать многоразовые биты обработчиков исключений, которые могут быть скомпилированы вместе с помощью orElse или andThen, тогда более подходящий второй метод.

Ответ 2

Как насчет соответствия шаблону?

val (x, y) = try generateX -> generateY catch { /*exception handling*/ }

или

val (x, y) = try (generateX, generateY) catch { /*exception handling*/ }

Ответ 3

Ответ agilesteel подходит, если вы просто хотите поймать любое исключение, которое было брошено и сделать что-то процедурное в блоке catch. Однако вы можете захотеть разобраться с исключениями по отдельности в более позднее время, и в этом случае вы можете подумать о том, чтобы сделать ваши типы Option или Either.

Встроенный способ сделать это с объектом Catch. См. Исключительные документы.

Как вы его используете, это будет зависеть от того, что вы хотите, когда возникает исключение. Например

import util.control.Exception.allCatch

def handleInfinities(n: Int) = {
  val x = allCatch.either { 100 / n }        // Either[Throwable, Int]
  val y = allCatch.either { 100 / (n - 1) }

  Seq(x, y) map { case Left(_) => Int.MaxValue; case Right(z) => z }
}

Тогда handleInfinities(1) дает

Seq[Int] = List(100, 2147483647)

Обратите внимание, что назначения переменных и обработка исключений теперь полностью разделены.

Ответ 4

import util.control.Exception._
def handler[A]: Catch[A] = handling(classOf[Exception]) by exceptionHandlingFunc
val x = handler[X] apply { generateX }
val y = handler[Y] apply { generateY }