【问题标题】:Custom Exception in scalascala中的自定义异常
【发布时间】:2016-07-07 10:48:17
【问题描述】:

如何在扩展Exception 类的 Scala 中创建自定义异常,并在发生异常时抛出它们并捕获它们。

java 中的示例:

class CustomException extends Exception {

  public final static String _FAIL_TO_INSERT = "FAIL_TO_INSERT";

}

【问题讨论】:

    标签: scala exception


    【解决方案1】:
    class MyException(message: String) extends Exception(message) {
    
      def this(message: String, cause: Throwable) {
        this(message)
        initCause(cause)
      }
    
      def this(cause: Throwable) {
        this(Option(cause).map(_.toString).orNull, cause)
      }
    
      def this() {
        this(null: String)
      }
    }
    

    这与@Jacek L. 的答案几乎相同。我只是想就这个答案背后的动机添加更多信息。

    为什么有这么多构造函数?

    Throwable 的写法很有趣。它有 4 个构造函数 -- 忽略带有boolean 切换的那个 -- 在nulls 中,它们每个的行为都略有不同,而这些差异只能通过多个构造函数来维护。

    如果 Scala 允许通过super 调用超类构造函数,它会更简洁一些,但它不会:(

    为什么不是案例类?

    • 完全维护构造函数关于nulls 的行为是不可能的;具体来说,def this()def this(message: String) 都必须将cause 设置为null,而最初设置为this
    • toString 不会被覆盖。
    • 消息和原因已通过getMessagegetCause 公开。添加另一个引用是多余的。
    • equals 将被覆盖并且将表现不同
      意思是,new Exception("m") == new Exception("m") // false
      new CaseException("m") == new CaseException("m") // true

    如果希望通过模式匹配来访问消息和原因,可以简单地实现unapply 方法:

    object MyException {
      def unapply(e: MyException): Option[(String,Throwable)] = Some((e.getMessage, e.getCause))
    }
    

    【讨论】:

    • 您能解释一下this(null: String) 部分吗?谢谢
    • @NickGinanto 在MyException 类中,有两个带有单个参数的构造函数——一个带有message: String 的主要构造函数,一个带有cause: Throwable 的构造函数。编译器无法推断对this(null) 的调用,因为它适用于这两个构造函数。指定null 的类型告诉编译器调用哪个构造函数。
    【解决方案2】:
    final case class CustomException(private val message: String = "", 
                               private val cause: Throwable = None.orNull)
                          extends Exception(message, cause) 
    

    试着抓住:

    try {
        throw CustomException("optional")
    } catch {
        case c: CustomException =>
              c.printStackTrace
    }
    

    【讨论】:

    • 我实际上会避免使用案例类。 toString 已经实现,而 equals 将被覆盖,其行为与其他异常不同(Exception 使用默认的 Object.equals)。此外,通过getMessagegetCause 已经可以公开访问消息和原因。如果您想要模式匹配,请实现unapply 方法。
    • 异常不是设计成map中的key,为什么要比较异常呢?方法 toString 返回与 Exception 相同的结果(您测试过吗?)。我将更改消息的可见性并导致私人隐藏详细信息。
    • @Andrez 我从来没有说过我想比较Exceptions。 equals 行为可能是最不重要的区别。是的,toString 的行为与Exception.toString 完全一样——这正是我所说的——这是使用case class 的原因之一。将 case class 值设为私有只会使其完全多余。还有我在my full answer 中讨论的构造函数的问题。
    【解决方案3】:

    你像这样定义你的自定义异常

    case class CustomException(s: String)  extends Exception(s)
    

    你可以像这样抛出你的异常:

    try{
    ...
    } catch{
    case x:Exception => throw new CustomException("whatever")
    }
    

    【讨论】:

      【解决方案4】:

      与其他答案类似,但我更喜欢使用伴随对象而不是替代构造函数。

      class MyException(message: String, cause: Throwable = null) extends Exception(message, cause)
      
      object MyException {
        def apply(message: String): MyException = new MyException(message)
        def apply(message: String, cause: Throwable): MyException = new MyException(message, cause)
      }
      

      【讨论】:

        【解决方案5】:

        您可能想要创建一个密封的特征:

        sealed trait MyException {
          // This is called a "self annotation". You can use "self" or "dog" or whatever you want.
          // It requires that those who extend this trait must also extend Throwable, or a subclass of it.
          self: Throwable =>
          val message: String
          val details: JsValue
        }
        

        那么您可以拥有任意数量的case classes,不仅可以扩展Exception,还可以扩展您的新特征。

        case class CustomException(message: String) extends Exception(message) with MyException {
          override val details: JsValue = Json.obj("message" -> message, "etc" -> "Anything else")
        }
        

        现在,使用 Scala 的全部意义在于向更函数化的编程风格迈进,这将使您的应用更具并发性,因此,如果您需要使用新的自定义异常,您可能想尝试这样的事情:

          def myExampleMethod(s: Option[String]): Future[Boolean] = Try {
            s match {
              case Some(text) =>
                text.length compareTo 5 match {
                  case 1 => true
                  case _ => false
                }
              case _ => throw CustomException("Was expecting some text")
            }
          }
          match {
            case Success(bool) => Future.successful(bool)
            case Failure(e) => Future.failed(e)
          }
        

        【讨论】:

        • 它不必被密封,但如果您需要对异常进行模式匹配,它会帮助编译器。我喜欢这个答案,因为特征本身并没有扩展异常,但需要使用该特征的人来扩展它。这样做的好处是,因为 scala 要求您使用特定的构造函数进行扩展,所以使用此解决方案,您不会只与异常所具有的众多构造函数之一结婚。
        • 我认为val: 中有一个不必要的: 错字。我无法编辑问题,因为显然 建议的编辑队列已满
        【解决方案6】:

        除了以上所有答案之外,如果您想要有一个错误层次结构,抽象类会有所帮助。

        abstract class GenericError(message: String) extends Exception(message)
        
        case class SpecificErrorA(message: String) extends GenericError(message)
        
        case class SpecificErrorB(message: String) extends GenericError(message)
        
        
        throw new SpecificErrorA("error on A") // OR
        throw new SpecificErrorB("error on B")
        

        使用特征而不是抽象类也可以做到这一点,但它们的局限性在于它们没有构造函数参数。

        可能在任何地方都使用 GenericError 并在应用程序/控制器边界上解构(模式匹配)它。

        【讨论】:

          【解决方案7】:

          为了反映 Exception 中的所有原始构造函数,我将使用以下模式实现自定义异常:

          class CustomException(msg: String) extends Exception(msg) {
            def this(msg: String, cause: Throwable) = {
              this(msg)
              initCause(cause)
            }
          
            def this(cause: Throwable) = {
              this(Option(cause).map(_.toString).orNull)
              initCause(cause)
            }
          
            def this() = {
              this(null: String)
            }
          }
          

          这也可以通过前面答案中提到的特征来实现。在这种情况下,我只是不创建单独的类:

          trait SomeException { self: Throwable =>
            def someDetail: SomeDetail
          }
          

          那么,投掷时:

          throw new Exception(...) with SomeException {
            override val someDetail = ...
          }
          

          当匹配时:

          try {
            ...
          } catch {
            case ex: Throwable with SomeException =>
              ex.getCause
              ex.getMessage
              ex.someDetail
          }
          

          这里的优点是您不会坚持父异常的任何特定构造函数。

          或多或少类似。

          【讨论】:

            猜你喜欢
            • 2011-04-19
            • 2010-12-09
            • 1970-01-01
            • 2012-12-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-03
            • 2016-07-27
            相关资源
            最近更新 更多