Инициализация 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 }