我的自定义异常类型应该是 case class
吗?
优点是我可以使用抽取器。
缺点是我会得到不正确的相等语义,但我可以通过覆盖 equals
来避免这种情况。
因此,在概念层面上,将它们设置为 case class
是否有意义呢?
我的自定义异常类型应该是 case class
吗?
优点是我可以使用抽取器。
缺点是我会得到不正确的相等语义,但我可以通过覆盖 equals
来避免这种情况。
因此,在概念层面上,将它们设置为 case class
是否有意义呢?
当然,这是非常主观的,但在我的看法中,将异常类作为case类是很好的实践。 主要原因是当你捕获一个异常时,你正在进行模式匹配,而case类在模式匹配中使用起来更加方便。 以下是一个例子,利用了在使用case类异常时,在catch块中可以使用完整的模式匹配能力:
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块是您通常基于类型而决定捕获哪些异常的地方,但也可以基于其字段的值或其他任何东西进行捕获,就像我的示例一样。关键在于异常类的相等语义与此关系很小。
通过为例外情况创建子类继承层次结构来指示错误条件的更大具体性是一种常见的习语,但如果使用case类创建例外情况,就会失去这个习惯用法。因为case类无法被继承。
object IOErrorType extends Enumeration {
val FileNotFound, DeviceError, LockedFile = Value
}
object IOError {
def unapply(err: IOError): Option[(String, IOErrorType.Value)] = Some(err.message, err.errorType)
}
class IOError(val message: String, val errorType: IOErrorType.Value) extends Exception(message)
def doSomeIO() { throw new IOError("Oops, file not found!", IOErrorType.FileNotFound) }
try {
doSomeIO()
} catch {
case IOError( msg, IOErrorType.FileNotFound ) =>
println("File not found, please check the path! (" + msg + ")")
}