У меня есть следующий макет код Scala в файле test.scala
:
class Transaction {
def begin() {}
def commit() {}
def rollback() {}
}
object Test extends Application {
def doSomething() {}
val t = new Transaction()
t.begin()
try {
doSomething()
t.commit()
} catch {
case _ => t.rollback()
}
}
Если я компилирую это на Scala 2.8 RC1 с scalac -Xstrict-warnings test.scala
Я получу соблюдающее предупреждение:
test.scala:16: warning: catch clause swallows everything: not advised.
case _ => t.rollback()
^
one warning found
Так, если всеобъемлющие выражения не рекомендуются, как я, как предполагается, реализую такой шаблон вместо этого? И кроме этого, почему такие выражения не рекомендуются во всяком случае?
Предупреждение существует, потому что вы, вероятно, не хотите все ловить. Например, обычно нецелесообразно пытаться отловить что-либо в java.lang.Error
, так как часто бывает трудно оправиться от таких вещей. (Велика вероятность того, что вас выкинут из блока catch из-за другого исключения.)
Кроме того, поскольку вы не можете эффективно поймать все, это небезопасный способ реализации атомарных / отказоустойчивых транзакций. Лучше использовать что-то вроде
try {
t.commit()
} finally {
if (!t.checkCommitted()) {
t.rollback()
if (!t.checkRolledback()) throw new FUBARed(t)
}
}
с дополнительным тестированием при чтении в новом t
, чтобы убедиться, что он находится в разумном состоянии.
Во-первых, обратите внимание, что это предупреждение, а не ошибка. И даже в этом случае предупреждение возникает только с параметром -Xstrict-warings. Другими словами, это означает, что возможно вы делаете логическую ошибку, но решать вам.
Как уже заметили другие, в большинстве случаев не имеет смысла перехватывать все исключения, и вам следует сделать что-то вроде этого:
t.begin()
try {
doSomething()
t.commit()
} catch {
case e: DuplicatedKeyError => ...
case e: BrokenConnectionError => ...
case e: DumbInputDetectedError => ...
}
т.е. эффективно обрабатывать все известные типы ошибок.
Но если вы уверены, что хотите игнорировать (или обрабатывать таким же образом) все возможные исключения, просто игнорируйте предупреждение.
У меня нет компилятора, чтобы проверить это, но не следует ли вам повторно генерировать исключение после отката транзакции? т.е. это должно быть
val t = new Transaction()
t.begin()
try {
doSomething()
t.commit()
} catch {
case e => t.rollback(); throw e
}
. Если вы перехватываете все исключения, вам следует обратить внимание на документацию для ControlThrowable
. Предположительно, вы хотите, чтобы ваша транзакция откатывалась при аварийном завершении, но не хотите, чтобы она откатывалась из-за нелокального возврата или util.control.Breaks.break
. Если это так, вы можете сделать что-то вроде следующего:
val t = new Transaction()
t.begin()
try {
doSomething()
t.commit()
} catch {
case ce : ControlThrowable => throw ce // propagate
case e => t.rollback(); throw e // roll-back and propagate
}