在java中,异常至少包含以下四个构造函数:
Exception()
Exception(String message)
Exception(String message, Throwable cause)
Exception(Throwable cause)
如果你想定义自己的扩展,你只需要声明一个后代异常并实现每个所需的构造函数调用相应的超级构造函数
你如何在scala中实现同样的目标?
到目前为止,我看到了this article和SO answer,但我怀疑必须有一种更简单的方法来实现这样一个共同的事情
答案 0 :(得分:58)
cause
的默认值为null。对于message
,它可以是cause.toString()
或null:
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
所以你可以使用默认值:
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
答案 1 :(得分:11)
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)
}
这样你就可以使用“new MissingConfigurationException”或来自伴侣对象的apply方法
无论如何,我仍然感到惊讶的是,没有更简单的方法来实现它
答案 2 :(得分:6)
您可以使用Throwable.initCause
。
class MyException (message: String, cause: Throwable)
extends RuntimeException(message) {
if (cause != null)
initCause(cause)
def this(message: String) = this(message, null)
}
答案 3 :(得分:5)
对我而言,似乎有三种不同的需求彼此之间存在动态紧张:
RuntimeException
的扩展者的便利性;即要编写的最小代码,以创建RuntimeException
null
泄露到他们的代码中如果一个人不关心3号,那么this answer(这个人的同伴)似乎很简洁。
但是,如果在尝试尽可能接近第1和第2位时将值设置为3,则下面的解决方案会有效地将Java null
泄漏封装到您的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)
}
如果您想要消除必须使用实际使用new
的{{1}},请添加此伴随对象(它只是将所有应用调用转发给现有的" master&# 34;类构造函数):
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
)
}
运算符,以便于将来可能的重构。如果所说的重构强烈地发生在Factory模式中,那将特别有用。我的最终结果虽然更详细,但对于客户来说应该是非常好的。
new
探索更复杂的RuntimeException模式:
从原始问题到希望为包或API创建专门的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
)
生态系统只是一个小小的飞跃。我们的想法是定义一个" root" RuntimeException
,可以从中创建特定后代异常的新生态系统。对我而言,使用RuntimeException
和catch
更容易利用特定类型的错误非常重要。
例如,我定义了match
方法,该方法在允许创建案例类之前验证一组条件。每个失败的条件都会生成validate
个实例。然后该方法返回RuntimeException
列表。这使客户能够决定他们如何处理响应; RuntimeInstance
列表保持异常,扫描列表中的特定内容并throw
或者只是将整个事情推到调用链上而不需要使用非常昂贵的JVM throw
命令。
此特定问题空间有三个不同的throw
后代,一个抽象(RuntimeException
)和两个具体(FailedPrecondition
和FailedPreconditionMustBeNonEmptyList
)。
第一个FailedPreconditionsException
是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
)
是间接FailedPreconditionMustBeNonEmptyList
后代,是RuntimeException
的直接具体实现。它定义了一个伴侣对象和一个类。伴随对象扩展了特征FailedPrecondition
。该类只是扩展抽象类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
)
是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))
和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))
的样子:
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
)
我远远没有回答原来的问题,因为我发现在Scala-ifying throw FailedPreconditionMustBeNonEmptyString()
具体或延伸到更一般的&#34;异常生态系统& #34;在Java中我变得非常舒服。
我希望能够在我的解决方案集中听到反馈(除了变体,&#34;哇!这对我来说太冗长了。&#34;)。我希望任何额外的优化或方法可以减少冗长,而不会丢失我为此模式的客户生成的任何价值或简洁性。
答案 4 :(得分:0)
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")
}
}
实例化MyClass将输出“oops”。
答案 5 :(得分:0)
这是@ roman-borisov的类似方法,但更类型安全。
case class ShortException(message: String = "", cause: Option[Throwable] = None)
extends Exception(message) {
cause.foreach(initCause)
}
然后,您可以用Java方式创建例外:
throw ShortException()
throw ShortException(message)
throw ShortException(message, Some(cause))
throw ShortException(cause = Some(cause))