Ответ 1
Это, конечно, очень субъективно, но, на мой взгляд, хорошая практика состоит в том, чтобы классы исключений были классами case. Основное обоснование заключается в том, что когда вы поймаете исключение, вы выполняете сопоставление с образцом, а классы case гораздо приятнее использовать при сопоставлении шаблонов. Здесь приведен пример, который использует преимущество использования полной мощности сопоставления шаблонов в блоке catch при использовании исключения класса case:
object IOErrorType extends Enumeration {
val FileNotFound, DeviceError, LockedFile = Value
}
case class IOError(message: String, errorType: IOErrorType.Value) extends Exception(message)
def doSomeIO() { throw IOError("Oops, file not found!", IOErrorType.FileNotFound) }
try {
doSomeIO()
} catch {
case IOError( msg, IOErrorType.FileNotFound ) =>
println("File not found, please check the path! (" + msg + ")")
}
В этом примере у нас есть только одно исключение, но оно содержит поле errorType
, когда вы хотите узнать тип точной ошибки (обычно это моделируется через иерархию исключения, я не говорю об этом лучше или хуже, пример просто иллюстративный). Поскольку IOError
является классом case, я могу просто сделать case IOError( msg, IOErrorType.FileNotFound )
, чтобы поймать исключение только для типа ошибки IOErrorType.FileNotFound
. Без экстракторов, которые мы бесплатно получаем с классами case, мне приходилось каждый раз улавливать исключение, а затем перескакивать в случае, если мне действительно неинтересно, что определенно более подробно.
Вы говорите, что классы case дают неверную семантику равенства. Я так не думаю. Вы, так как автор класса исключений принимает решение о семантике семантики. В конце концов, когда вы поймаете исключение, блок catch - это то, где вы решаете, какие исключения для catch обычно основываются только на типе, но могут быть основаны на значении его полей или что-то еще, как в моем примере. Дело в том, что семантика равенства класса исключения имеет мало общего с этим.