使用scala中的重载构造函数定义自己的异常

ope*_*sas 36 scala exception

在java中,异常至少包含以下四个构造函数:

Exception() 
Exception(String message) 
Exception(String message, Throwable cause) 
Exception(Throwable cause) 
Run Code Online (Sandbox Code Playgroud)

如果你想定义自己的扩展,你只需要声明一个后代异常并实现每个所需的构造函数调用相应的超级构造函数

你怎么能在scala中实现同样的东西?

到目前为止,我看到了这篇文章和这个SO答案,但我怀疑必须有一种更简单的方法来实现这样一个共同的事情

sen*_*nia 58

默认值为causenull.因为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
Run Code Online (Sandbox Code Playgroud)

所以你可以使用默认值:

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
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是`RuntimeException(cause)`的行为与`Runtime(null,cause)`的行为不同,因为前者设置`detailMessage = cause.toString()`,后者留下`detailMessage = null`.因此,上面的代码与在Java中调用适当的超类构造函数的行为不同. (2认同)

ope*_*sas 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)
}
Run Code Online (Sandbox Code Playgroud)

这样,您可以使用"new MissingConfigurationException"或来自伴随对象的apply方法

无论如何,我仍然感到惊讶的是,没有一种更简单的方法来实现它


小智 6

你可以用Throwable.initCause.

class MyException (message: String, cause: Throwable) 
  extends RuntimeException(message) {
    if (cause != null)
      initCause(cause)

    def this(message: String) = this(message, null)  
}
Run Code Online (Sandbox Code Playgroud)


cha*_*ium 5

对我而言,似乎有三种不同的需求彼此之间存在动态紧张:

  1. 扩展器的便利性RuntimeException; 即要编写的最小代码来创建后代RuntimeException
  2. 客户感知易用性; 即在呼叫站点写入的最小代码
  3. 客户倾向于避免将可怕的Java泄露null到他们的代码中

如果一个人不关心3号,那么这个答案(与这个答案相同)似乎非常简洁.

但是,如果在尝试尽可能接近数字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)
}
Run Code Online (Sandbox Code Playgroud)

如果你想消除不必使用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
    )
}
Run Code Online (Sandbox Code Playgroud)

就个人而言,我更愿意new在尽可能多的代码中抑制运算符的使用,以便于将来可能的重构.如果所说的重构强烈地发生在Factory模式中,那么它尤其有用.我的最终结果虽然更详细,但对于客户来说应该是非常好的.

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
)
Run Code Online (Sandbox Code Playgroud)

探索更复杂的RuntimeException模式:

从原始问题到希望RuntimeException为包或API 创建专用s 生态系统只是一个小小的飞跃.我们的想法是定义一个"根" RuntimeException,从中可以创建一个特定后代异常的新生态系统.对我来说,使用catch并且match更容易利用特定类型的错误非常重要.

例如,我有一个validate定义的方法,它在允许创建案例类之前验证一组条件.每个失败的条件都会生成一个RuntimeException实例.然后RuntimeInstance由方法返回s 列表.这使客户能够决定他们如何处理响应; throw列表保持异常,扫描列表中的特定内容throw,或者只是将整个事物推到调用链上而不需要使用非常昂贵的JVM throw命令.

这个特殊的问题空间有三个不同的后代RuntimeException,一个是abstract(FailedPrecondition),另一个是具体的(FailedPreconditionMustBeNonEmptyListFailedPreconditionsException).

第一个,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
)
Run Code Online (Sandbox Code Playgroud)

第二个,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
  )
Run Code Online (Sandbox Code Playgroud)

第三,FailedPreconditionsException是一种直接后代RuntimeException,其包装了ListFailedPrecondition秒,然后动态地管理异常消息的发射.

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))
Run Code Online (Sandbox Code Playgroud)

然后把所有的一起作为一个整体整齐的事情了,我把两个FailedPreconditionFailedPreconditionMustBeNonEmptyList对象之内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))
Run Code Online (Sandbox Code Playgroud)

这就是客户端使用上面的代码创建自己的异常派生的看法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
  )
Run Code Online (Sandbox Code Playgroud)

然后使用此异常如下所示:

throw FailedPreconditionMustBeNonEmptyString()
Run Code Online (Sandbox Code Playgroud)

我已经远远没有回答原来的问题了,因为我发现很难找到任何接近于特定和全面的Scala-ifying RuntimeException具体或扩展到更普遍的"异常生态系统",在Java中我变得非常舒服.

我很乐意在我的解决方案集上听到反馈(除了变化之外,"哇!这对我来说太冗长了.").而且我会喜欢任何额外的优化或方法来减少冗余,而不会丢失我为此模式的客户端生成的任何值或简洁性.