在 Scala 中使用重载的构造函数定义您自己的异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10925268/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
define your own exceptions with overloaded constructors in scala
提问by opensas
In java exceptions have at least these four constructors:
在 java 中,异常至少有这四个构造函数:
Exception()
Exception(String message)
Exception(String message, Throwable cause)
Exception(Throwable cause)
If you want to define your own extensions, you just have to declare a descendent exceptions and implement each desired constructor calling the corresponden super constructor
如果你想定义你自己的扩展,你只需要声明一个后代异常并实现每个所需的构造函数调用对应的超级构造函数
How can you achieve the same thing in scala?
你怎么能在scala中实现同样的事情?
so far now I saw this articleand this SO answer, but I suspect there must be an easier way to achieve such a common thing
回答by senia
Default value for causeis null. And for messageit is either cause.toString()or null:
的默认值为cause空。因为message它要么是要么为cause.toString()空:
val e1 = new RuntimeException()
e.getCause
// res1: java.lang.Throwable = null
e.getMessage
//res2: java.lang.String = null
val cause = new RuntimeException("cause msg")
val e2 = new RuntimeException(cause)
e.getMessage()
//res3: String = java.lang.RuntimeException: cause msg
So you can just use default values:
所以你可以只使用默认值:
class MyException(message: String = null, cause: Throwable = null) extends
RuntimeException(MyException.defaultMessage(message, cause), cause)
object MyException {
def defaultMessage(message: String, cause: Throwable) =
if (message != null) message
else if (cause != null) cause.toString()
else null
}
// usage:
new MyException(cause = myCause)
// res0: MyException = MyException: java.lang.RuntimeException: myCause msg
回答by opensas
well, this is the best I've found so far
嗯,这是我目前找到的最好的
class MissingConfigurationException private(ex: RuntimeException) extends RuntimeException(ex) {
def this(message:String) = this(new RuntimeException(message))
def this(message:String, throwable: Throwable) = this(new RuntimeException(message, throwable))
}
object MissingConfigurationException {
def apply(message:String) = new MissingConfigurationException(message)
def apply(message:String, throwable: Throwable) = new MissingConfigurationException(message, throwable)
}
this way you may use the "new MissingConfigurationException" or the apply method from the companion object
通过这种方式,您可以使用“新的 MissingConfigurationException”或伴随对象的 apply 方法
Anyway, I'm still surprised that there isn't a simpler way to achieve it
无论如何,我仍然很惊讶没有更简单的方法来实现它
回答by Roman Borisov
You can use Throwable.initCause.
您可以使用Throwable.initCause.
class MyException (message: String, cause: Throwable)
extends RuntimeException(message) {
if (cause != null)
initCause(cause)
def this(message: String) = this(message, null)
}
回答by chaotic3quilibrium
To me, it appears there are three different needs which have a dynamic tension with each other:
对我而言,似乎存在三种不同的需求,它们之间存在动态张力:
- The convenience of the extender of
RuntimeException; i.e. minimal code to be written to create a descendant ofRuntimeException - Client's perceived ease of use; i.e. minimal code to be written at the call-site
- Client's preference to avoiding leaking the dreaded Java
nullinto their code
- 扩展器的便利性
RuntimeException;即最少的代码来创建一个后代RuntimeException - 客户感知的易用性;即在调用站点编写的最少代码
- 客户倾向于避免将可怕的 Java 泄漏
null到他们的代码中
If one doesn't care about number 3, then this answer(a peer to this one) seems pretty succinct.
如果一个人不关心数字 3,那么这个答案(这个答案的同行)似乎非常简洁。
However, if one values number 3 while trying to get as close to number 1 and 2 as possible, the solution below effectively encapsulates the Java nullleak into your Scala API.
但是,如果在尝试尽可能接近数字 1 和 2 时将数字 3 设为值,则下面的解决方案有效地将 Javanull泄漏封装到您的 Scala API 中。
class MyRuntimeException (
val optionMessage: Option[String],
val optionCause: Option[Throwable],
val isEnableSuppression: Boolean,
val isWritableStackTrace: Boolean
) extends RuntimeException(
optionMessage match {
case Some(string) => string
case None => null
},
optionCause match {
case Some(throwable) => throwable
case None => null
},
isEnableSuppression,
isWritableStackTrace
) {
def this() =
this(None, None, false, false)
def this(message: String) =
this(Some(message), None, false, false)
def this(cause: Throwable) =
this(None, Some(cause), false, false)
def this(message: String, cause: Throwable) =
this(Some(message), Some(cause), false, false)
}
And if you would like to eliminate having to use newwhere MyRuntimeExceptionis actually used, add this companion object (which just forwards all of the apply calls to the existing "master" class constructor):
如果你想消除不必使用new,其中MyRuntimeException实际使用,添加这个同伴对象(这只是转发所有的呼叫适用于现有的“主”类的构造函数):
object MyRuntimeException {
def apply: MyRuntimeException =
MyRuntimeException()
def apply(message: String): MyRuntimeException =
MyRuntimeException(optionMessage = Some(message))
def apply(cause: Throwable): MyRuntimeException =
MyRuntimeException(optionCause = Some(cause))
def apply(message: String, cause: Throwable): MyRuntimeException =
MyRuntimeException(optionMessage = Some(message), optionCause = Some(cause))
def apply(
optionMessage: Option[String] = None,
optionCause: Option[Throwable] = None,
isEnableSuppression: Boolean = false,
isWritableStackTrace: Boolean = false
): MyRuntimeException =
new MyRuntimeException(
optionMessage,
optionCause,
isEnableSuppression,
isWritableStackTrace
)
}
Personally, I prefer to actually suppress the use of the newoperator in as much code as possible so as to ease possible future refactorings. It is especially helpful if said refactoring happens to strongly to the Factory pattern. My final result, while more verbose, should be quite nice for clients to use.
就我个人而言,我更喜欢new在尽可能多的代码中实际抑制操作符的使用,以简化未来可能的重构。如果所说的重构非常适用于工厂模式,这将特别有帮助。我的最终结果虽然比较冗长,但对于客户使用来说应该非常好。
object MyRuntimeException {
def apply: MyRuntimeException =
MyRuntimeException()
def apply(message: String): MyRuntimeException =
MyRuntimeException(optionMessage = Some(message))
def apply(cause: Throwable): MyRuntimeException =
MyRuntimeException(optionCause = Some(cause))
def apply(message: String, cause: Throwable): MyRuntimeException =
MyRuntimeException(optionMessage = Some(message), optionCause = Some(cause))
def apply(
optionMessage: Option[String] = None,
optionCause: Option[Throwable] = None,
isEnableSuppression: Boolean = false,
isWritableStackTrace: Boolean = false
): MyRuntimeException =
new MyRuntimeException(
optionMessage,
optionCause,
isEnableSuppression,
isWritableStackTrace
)
}
class MyRuntimeException private[MyRuntimeException] (
val optionMessage: Option[String],
val optionCause: Option[Throwable],
val isEnableSuppression: Boolean,
val isWritableStackTrace: Boolean
) extends RuntimeException(
optionMessage match {
case Some(string) => string
case None => null
},
optionCause match {
case Some(throwable) => throwable
case None => null
},
isEnableSuppression,
isWritableStackTrace
)
Exploring a more sophisticated RuntimeException pattern:
探索更复杂的 RuntimeException 模式:
It's only a small leap from the original question to desire to create an ecosystem of specialized RuntimeExceptions for a package or API. The idea is to define a "root" RuntimeExceptionfrom which a new ecosystem of specific descendant exceptions can be created. For me, it was important to make using catchand matchmuch easier to exploit for specific types of errors.
从最初的问题到希望RuntimeException为包或 API创建一个专门的生态系统,这只是一个小小的飞跃。这个想法是定义一个“根” RuntimeException,从中可以创建一个特定后代异常的新生态系统。对我来说,重要的是要针对特定类型的错误进行使用catch和match利用。
For example, I have a validatemethod defined which verifies a set of conditions prior to allowing a case class to be created. Each condition that fails generates a RuntimeExceptioninstance. And then the List of RuntimeInstances are returned by the method. This gives the client the ability to decide how they would like to handle the response; throwthe list holding exception, scan the list for something specific and throwthat or just push the entire thing up the call chain without engaging the very expensive JVM throwcommand.
例如,我validate定义了一个方法,它在允许创建案例类之前验证一组条件。每个失败的条件都会生成一个RuntimeException实例。然后RuntimeInstance方法返回s的List 。这使客户端能够决定他们希望如何处理响应;throw保留异常的列表,扫描列表中的特定内容throw,或者只是将整个内容推送到调用链上,而无需使用非常昂贵的 JVMthrow命令。
This particular problem space has three different descendants of RuntimeException, one abstract (FailedPrecondition) and two concrete (FailedPreconditionMustBeNonEmptyListand FailedPreconditionsException).
这个特定的问题空间具有 的三个不同的后代RuntimeException,一个抽象的 ( FailedPrecondition) 和两个具体的 ( FailedPreconditionMustBeNonEmptyListand FailedPreconditionsException)。
The first, FailedPrecondition, is a direct descendant to RuntimeException, very similar to MyRuntimeException, and is abstract (to prevent direct instantiations). FailedPreconditionhas a "companion object trait", FailedPreconditionObjectwhich acts as the instantiation factory (suppressing the newoperator).
第一个 ,FailedPrecondition是 的直接后代RuntimeException,与 非常相似MyRuntimeException,并且是抽象的(以防止直接实例化)。FailedPrecondition有一个“伴随对象特征”,FailedPreconditionObject它充当实例化工厂(抑制new操作符)。
trait FailedPreconditionObject[F <: FailedPrecondition] {
def apply: F =
apply()
def apply(message: String): F =
apply(optionMessage = Some(message))
def apply(cause: Throwable): F =
apply(optionCause = Some(cause))
def apply(message: String, cause: Throwable): F =
apply(optionMessage = Some(message), optionCause = Some(cause))
def apply(
optionMessage: Option[String] = None
, optionCause: Option[Throwable] = None
, isEnableSuppression: Boolean = false
, isWritableStackTrace: Boolean = false
): F
}
abstract class FailedPrecondition (
val optionMessage: Option[String],
val optionCause: Option[Throwable],
val isEnableSuppression: Boolean,
val isWritableStackTrace: Boolean
) extends RuntimeException(
optionMessage match {
case Some(string) => string
case None => null
},
optionCause match {
case Some(throwable) => throwable
case None => null
},
isEnableSuppression,
isWritableStackTrace
)
The second, FailedPreconditionMustBeNonEmptyList, is an indirect RuntimeExceptiondescendant and a direct concrete implementation of FailedPrecondition. It defines both a companion object and a class. The companion object extends the trait FailedPreconditionObject. And the class simply extends the abstract class FailedPreconditionand marks it finalto prevent any further extensions.
第二个 ,FailedPreconditionMustBeNonEmptyList是 的间接RuntimeException后代,是 的直接具体实现FailedPrecondition。它定义了一个伴生对象和一个类。伴随对象扩展了 trait FailedPreconditionObject。这个类只是简单地扩展了抽象类FailedPrecondition并标记它final以防止任何进一步的扩展。
object FailedPreconditionMustBeNonEmptyList extends FailedPreconditionObject[FailedPreconditionMustBeNonEmptyList] {
def apply(
optionMessage: Option[String] = None
, optionCause: Option[Throwable] = None
, isEnableSuppression: Boolean = false
, isWritableStackTrace: Boolean = false
): FailedPreconditionMustBeNonEmptyList =
new FailedPreconditionMustBeNonEmptyList(
optionMessage
, optionCause
, isEnableSuppression
, isWritableStackTrace
)
}
final class FailedPreconditionMustBeNonEmptyList private[FailedPreconditionMustBeNonEmptyList] (
optionMessage: Option[String]
, optionCause: Option[Throwable]
, isEnableSuppression: Boolean
, isWritableStackTrace: Boolean
) extends
FailedPrecondition(
optionMessage
, optionCause
, isEnableSuppression
, isWritableStackTrace
)
The third, FailedPreconditionsException, is a direct descendant to RuntimeExceptionwhich wraps a Listof FailedPreconditions and then dynamically manages the emitting of the exception message.
第三,FailedPreconditionsException是一种直接后代到RuntimeException它包装一个List的FailedPrecondition秒,然后动态地管理异常消息的发射。
object FailedPreconditionsException {
def apply(failedPrecondition: FailedPrecondition): FailedPreconditionsException =
FailedPreconditionsException(List(failedPrecondition))
def apply(failedPreconditions: List[FailedPrecondition]): FailedPreconditionsException =
tryApply(failedPreconditions).get
def tryApply(failedPrecondition: FailedPrecondition): Try[FailedPreconditionsException] =
tryApply(List(failedPrecondition))
def tryApply(failedPreconditions: List[FailedPrecondition]): Try[FailedPreconditionsException] =
if (failedPreconditions.nonEmpty)
Success(new FailedPreconditionsException(failedPreconditions))
else
Failure(FailedPreconditionMustBeNonEmptyList())
private def composeMessage(failedPreconditions: List[FailedPrecondition]): String =
if (failedPreconditions.size > 1)
s"failed preconditions [${failedPreconditions.size}] have occurred - ${failedPreconditions.map(_.optionMessage.getOrElse("")).mkString("|")}"
else
s"failed precondition has occurred - ${failedPreconditions.head.optionMessage.getOrElse("")}"
}
final class FailedPreconditionsException private[FailedPreconditionsException] (
val failedPreconditions: List[FailedPrecondition]
) extends RuntimeException(FailedPreconditionsException.composeMessage(failedPreconditions))
And then bringing all of that together as a whole and tidy things up, I place both FailedPreconditionand FailedPreconditionMustBeNonEmptyListwithin object FailedPreconditionsException. And this is what the final result looks like:
然后把所有的一起作为一个整体整齐的事情了,我把两个FailedPrecondition和FailedPreconditionMustBeNonEmptyList对象之内FailedPreconditionsException。这就是最终结果的样子:
object FailedPreconditionsException {
trait FailedPreconditionObject[F <: FailedPrecondition] {
def apply: F =
apply()
def apply(message: String): F =
apply(optionMessage = Some(message))
def apply(cause: Throwable): F =
apply(optionCause = Some(cause))
def apply(message: String, cause: Throwable): F =
apply(optionMessage = Some(message), optionCause = Some(cause))
def apply(
optionMessage: Option[String] = None
, optionCause: Option[Throwable] = None
, isEnableSuppression: Boolean = false
, isWritableStackTrace: Boolean = false
): F
}
abstract class FailedPrecondition (
val optionMessage: Option[String]
, val optionCause: Option[Throwable]
, val isEnableSuppression: Boolean
, val isWritableStackTrace: Boolean
) extends RuntimeException(
optionMessage match {
case Some(string) => string
case None => null
},
optionCause match {
case Some(throwable) => throwable
case None => null
},
isEnableSuppression,
isWritableStackTrace
)
object FailedPreconditionMustBeNonEmptyList extends FailedPreconditionObject[FailedPreconditionMustBeNonEmptyList] {
def apply(
optionMessage: Option[String] = None
, optionCause: Option[Throwable] = None
, isEnableSuppression: Boolean = false
, isWritableStackTrace: Boolean = false
): FailedPreconditionMustBeNonEmptyList =
new FailedPreconditionMustBeNonEmptyList(
optionMessage
, optionCause
, isEnableSuppression
, isWritableStackTrace
)
}
final class FailedPreconditionMustBeNonEmptyList private[FailedPreconditionMustBeNonEmptyList] (
optionMessage: Option[String]
, optionCause: Option[Throwable]
, isEnableSuppression: Boolean
, isWritableStackTrace: Boolean
) extends
FailedPrecondition(
optionMessage
, optionCause
, isEnableSuppression
, isWritableStackTrace
)
def apply(failedPrecondition: FailedPrecondition): FailedPreconditionsException =
FailedPreconditionsException(List(failedPrecondition))
def apply(failedPreconditions: List[FailedPrecondition]): FailedPreconditionsException =
tryApply(failedPreconditions).get
def tryApply(failedPrecondition: FailedPrecondition): Try[FailedPreconditionsException] =
tryApply(List(failedPrecondition))
def tryApply(failedPreconditions: List[FailedPrecondition]): Try[FailedPreconditionsException] =
if (failedPreconditions.nonEmpty)
Success(new FailedPreconditionsException(failedPreconditions))
else
Failure(FailedPreconditionMustBeNonEmptyList())
private def composeMessage(failedPreconditions: List[FailedPrecondition]): String =
if (failedPreconditions.size > 1)
s"failed preconditions [${failedPreconditions.size}] have occurred - ${failedPreconditions.map(_.optionMessage.getOrElse("")).mkString("|")}"
else
s"failed precondition has occurred - ${failedPreconditions.head.optionMessage.getOrElse("")}"
}
final class FailedPreconditionsException private[FailedPreconditionsException] (
val failedPreconditions: List[FailedPreconditionsException.FailedPrecondition]
) extends RuntimeException(FailedPreconditionsException.composeMessage(failedPreconditions))
And this is what it would look like for a client to use the above code to create their own exception derivation called FailedPreconditionMustBeNonEmptyString:
这就是客户端使用上述代码创建他们自己的异常派生的样子,称为FailedPreconditionMustBeNonEmptyString:
object FailedPreconditionMustBeNonEmptyString extends FailedPreconditionObject[FailedPreconditionMustBeNonEmptyString] {
def apply(
optionMessage: Option[String] = None
, optionCause: Option[Throwable] = None
, isEnableSuppression: Boolean = false
, isWritableStackTrace: Boolean = false
): FailedPreconditionMustBeNonEmptyString =
new FailedPreconditionMustBeNonEmptyString(
optionMessage
, optionCause
, isEnableSuppression
, isWritableStackTrace
)
}
final class FailedPreconditionMustBeNonEmptyString private[FailedPreconditionMustBeNonEmptyString] (
optionMessage: Option[String]
, optionCause: Option[Throwable]
, isEnableSuppression: Boolean
, isWritableStackTrace: Boolean
) extends
FailedPrecondition(
optionMessage
, optionCause
, isEnableSuppression
, isWritableStackTrace
)
And then the use of this exception looks like this:
然后这个异常的使用看起来像这样:
throw FailedPreconditionMustBeNonEmptyString()
I went well beyond answering the original question because I found it so difficult to locate anything close to being both specific and comprehensive in Scala-ifying RuntimeExceptionin specific or extending into the more general "exception ecosystem" with which I grew so comfortable when in Java.
我远远超出了回答原始问题的范围,因为我发现很难在 Scala 化RuntimeException具体或扩展到更一般的“异常生态系统”中找到既具体又全面的任何东西,我在 Java 中变得如此自在。
I'd love to hear feedback (other than variations on, "Wow! That's way too verbose for me.") on my solution set. And I would love any additional optimizations or ways to reduce the verbosity WITHOUT LOSING any of the value or terseness I have generated for the clients of this pattern.
我很想听到关于我的解决方案集的反馈(除了“哇!这对我来说太冗长了。”)。我会喜欢任何额外的优化或减少冗长的方法,而不会失去我为这种模式的客户产生的任何价值或简洁性。
回答by Jose Fernandez
Scala pattern matching in try/catch blocks works on interfaces. My solution is to use an interface for the exception name then use separate class instances.
try/catch 块中的 Scala 模式匹配适用于接口。我的解决方案是为异常名称使用接口,然后使用单独的类实例。
trait MyException extends RuntimeException
class MyExceptionEmpty() extends RuntimeException with MyException
class MyExceptionStr(msg: String) extends RuntimeException(msg) with MyException
class MyExceptionEx(t: Throwable) extends RuntimeException(t) with MyException
object MyException {
def apply(): MyException = new MyExceptionEmpty()
def apply(msg: String): MyException = new MyExceptionStr(msg)
def apply(t: Throwable): MyException = new MyExceptionEx(t)
}
class MyClass {
try {
throw MyException("oops")
} catch {
case e: MyException => println(e.getMessage)
case _: Throwable => println("nope")
}
}
Instantiating MyClass will output "oops".
实例化 MyClass 将输出“oops”。
回答by matfax
Here is a similar approach to the one of @roman-borisov but more typesafe.
这是与@roman-borisov 类似的方法,但类型更安全。
case class ShortException(message: String = "", cause: Option[Throwable] = None)
extends Exception(message) {
cause.foreach(initCause)
}
Then, you can create Exceptions in the Java manner:
然后,您可以以 Java 方式创建异常:
throw ShortException()
throw ShortException(message)
throw ShortException(message, Some(cause))
throw ShortException(cause = Some(cause))

