我的自定义异常类型应该是case class
es 吗?
从好的方面来说,我得到了提取器。
不利的一面是,我得到了不正确的相等语义。但我可以通过覆盖来避免这种情况equals
。
那么,在概念层面上,将它们变成case class
es 有意义吗?
我的自定义异常类型应该是case class
es 吗?
从好的方面来说,我得到了提取器。
不利的一面是,我得到了不正确的相等语义。但我可以通过覆盖来避免这种情况equals
。
那么,在概念层面上,将它们变成case class
es 有意义吗?
这当然是非常主观的,但在我看来,将异常类作为案例类是一种很好的做法。主要理由是,当您捕获异常时,您正在进行模式匹配,并且案例类更适合用于模式匹配。这是一个在使用案例类异常时利用在 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 IOError( msg, IOErrorType.FileNotFound )
只为错误类型捕获异常IOErrorType.FileNotFound
。如果没有我们通过案例类免费获得的提取器,我将不得不每次都捕获异常,然后重新抛出以防我实际上不感兴趣,这肯定更冗长。
你说案例类给你不正确的相等语义。我不这么认为。你,作为异常类的编写者来决定什么相等语义是有意义的。毕竟当你捕获一个异常时,catch 块是你决定捕获哪些异常的地方,通常仅基于类型,但也可以基于其字段的值或其他值,如我的示例所示。关键是异常类的相等语义与此无关。
通过创建异常案例类而丢失的一个常见习惯用法是创建异常的子类层次结构的模式,其中子类化用于指示错误条件的更大特异性。案例类不能被子类化。
我喜欢 Régis Jean-Gilles 的回答。但是,如果您有充分的理由不创建案例类(参见 Dave Griffith 的回答),您可以使用普通类归档与上述示例相同的内容并取消应用:
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 + ")")
}