Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在scala中使用重载构造函数定义自己的异常_Scala_Exception - Fatal编程技术网

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

在scala中使用重载构造函数定义自己的异常,scala,exception,Scala,Exception,在java中,异常至少有以下四个构造函数: Exception() Exception(String message) Exception(String message, Throwable cause) Exception(Throwable cause) 如果您想定义自己的扩展,只需声明一个派生异常,并实现每个所需的构造函数调用相应的超级构造函数 如何在scala中实现同样的目标 到目前为止,我看到了这一点,但我怀疑一定有更简单的方法来实现这样一个普通的事情好吧,这是迄今为止我发现

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

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.在呼叫现场编写的最少代码
  • 客户希望避免将可怕的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)
    }
    
    如果您不想在实际使用
    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
    或者在不使用非常昂贵的JVM
    throw
    命令的情况下将整个内容推上调用链

    这个特定的问题空间有三个不同的
    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”…@opensas
    objectmyException{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))