在scala中使用重载构造函数定义自己的异常
在java中,异常至少有以下四个构造函数:在scala中使用重载构造函数定义自己的异常,scala,exception,Scala,Exception,在java中,异常至少有以下四个构造函数: Exception() Exception(String message) Exception(String message, Throwable cause) Exception(Throwable cause) 如果您想定义自己的扩展,只需声明一个派生异常,并实现每个所需的构造函数调用相应的超级构造函数 如何在scala中实现同样的目标 到目前为止,我看到了这一点,但我怀疑一定有更简单的方法来实现这样一个普通的事情好吧,这是迄今为止我发现
Exception()
Exception(String message)
Exception(String message, Throwable cause)
Exception(Throwable cause)
如果您想定义自己的扩展,只需声明一个派生异常,并实现每个所需的构造函数调用相应的超级构造函数
如何在scala中实现同样的目标
到目前为止,我看到了这一点,但我怀疑一定有更简单的方法来实现这样一个普通的事情好吧,这是迄今为止我发现的最好的方法
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方法
无论如何,我仍然感到惊讶的是,没有更简单的方法来实现它。因为
的默认值为空。对于消息
,它要么是原因.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
因此,您可以只使用默认值:
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
您可以使用Throwable.initCause
class MyException (message: String, cause: Throwable)
extends RuntimeException(message) {
if (cause != null)
initCause(cause)
def this(message: String) = this(message, null)
}
在我看来,似乎有三种不同的需求彼此之间存在着动态的张力:
运行时异常扩展程序的方便性
;i、 e.创建RuntimeException
客户感知的易用性;i、 e.在呼叫现场编写的最少代码
客户希望避免将可怕的Javanull
泄漏到他们的代码中
如果一个人不在乎数字3,那么(与此相同的)看起来相当简洁
但是,如果一个人在尝试尽可能接近数字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)
}
如果您不想在实际使用MyRuntimeException
的地方使用new
,请添加这个伴随对象(它只是将所有apply调用转发给现有的“master”类构造函数):
就我个人而言,我更喜欢在尽可能多的代码中实际禁止使用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
)
探索更复杂的运行时异常模式:
从最初的问题到希望为包或API创建一个专门的RuntimeException
s生态系统,这只是一个小小的飞跃。其思想是定义一个“根”RuntimeException
,从中可以创建特定后代异常的新生态系统。对我来说,重要的是让使用catch
和match
更容易利用特定类型的错误
例如,我定义了一个validate
方法,该方法在允许创建案例类之前验证一组条件。每个失败的条件都会生成一个RuntimeException
实例。然后该方法返回RuntimeInstance
s的列表。这使客户能够决定如何处理响应throw
list-holding异常,扫描列表以查找特定的内容,然后throw
或者在不使用非常昂贵的JVMthrow
命令的情况下将整个内容推上调用链
这个特定的问题空间有三个不同的RuntimeException
的后代,一个抽象(FailedPremissions
)和两个具体(FailedPremissionsMustbenOnEmptylist
和FailedPremissionsException
)
第一个,failedPremission
,是RuntimeException
的直接后代,与MyRuntimeException
非常相似,是抽象的(以防止直接实例化)FailedPremission
有一个“伴生对象特征”FailedPremissionObject
,它充当实例化工厂(抑制新的
操作符)
第三个是failedPremissionException
,是RuntimeException
的直接后代,它包装了failedPremissions
的列表,然后动态管理异常消息的发出
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))
然后将所有这些作为一个整体进行整理,我将failedPremissions
和failedPremissionmustbenonEmptylist
放在对象failedPremissionsException
中。这就是最终的结果:
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))
然后,此异常的使用如下所示:
throw FailedPreconditionMustBeNonEmptyString()
我远远超出了回答原始问题的范围,因为我发现很难找到任何既具体又全面的东西,比如具体地扩展RuntimeException
,或者扩展到更一般的“异常生态系统”,而我在Java中已经非常适应了这种生态系统
我很想在我的解决方案集上听到反馈(除了“哇!这对我来说太冗长了”之类的变化)。我希望有任何额外的优化或方法来减少冗长,而不丢失我为该模式的客户端生成的任何值或简洁性。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”。这里有一种类似于@roman borisov的方法,但更安全。
然后,您可以用Java方式创建异常:
throw ShortException()
throw ShortException(message)
throw ShortException(message, Some(cause))
throw ShortException(cause = Some(cause))
异常不应该扩展RuntimeException吗?很好的答案,而且,如果你将它声明为一个case类,你可以去掉“new”…@opensasobjectmyException{def apply(message:String=null,cause:Throwable=null)=newmyException(message,cause)}
就足够了。不幸的是RuntimeException(cause)
的行为不同于运行时(null,原因
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
)
throw FailedPreconditionMustBeNonEmptyString()
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")
}
}
case class ShortException(message: String = "", cause: Option[Throwable] = None)
extends Exception(message) {
cause.foreach(initCause)
}
throw ShortException()
throw ShortException(message)
throw ShortException(message, Some(cause))
throw ShortException(cause = Some(cause))