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

时间:2012-06-07 03:23:14

标签: scala exception

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

Exception() 
Exception(String message) 
Exception(String message, Throwable cause) 
Exception(Throwable cause) 

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

你如何在scala中实现同样的目标?

到目前为止,我看到了this articleSO answer,但我怀疑必须有一种更简单的方法来实现这样一个共同的事情

6 个答案:

答案 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)

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

  1. RuntimeException的扩展者的便利性;即要编写的最小代码,以创建RuntimeException
  2. 的后代
  3. 客户感知易用性;即在呼叫现场写的最小代码
  4. 客户希望避免将可怕的Java null泄露到他们的代码中
  5. 如果一个人不关心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,可以从中创建特定后代异常的新生态系统。对我而言,使用RuntimeExceptioncatch更容易利用特定类型的错误非常重要。

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

    此特定问题空间有三个不同的throw后代,一个抽象(RuntimeException)和两个具体(FailedPreconditionFailedPreconditionMustBeNonEmptyList)。

    第一个FailedPreconditionsExceptionFailedPrecondition的直接后代,与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))