scala 中的自定义异常

问题描述 投票:0回答:7

我如何在扩展

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

Java 中的示例:

class CustomException extends Exception {

  public final static String _FAIL_TO_INSERT = "FAIL_TO_INSERT";

}
scala exception
7个回答
73
投票
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
}

41
投票
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
开关的开关 -- 它们中的每一个与
null
的行为都略有不同,并且这些差异只能通过多个构造函数来维持。

如果 Scala 允许通过

super
调用超类构造函数,那会更干净一点,但事实并非如此:(

为什么不是案例班?

  • 完美地维护构造函数关于
    null
    的行为是不可能的;具体来说,
    def this()
    def this(message: String)
    都必须将
    cause
    设置为
    null
    ,而最初它设置为
    this
  • toString
    不会被覆盖。
  • 消息和原因已通过
    getMessage
    getCause
    公开。添加另一个引用这些是多余的。
  • 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))
}

11
投票

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

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 class
,您不仅需要扩展
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)
  }

9
投票

为了反映 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
}

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

或多或少是这样的。


6
投票

您可以像这样定义自定义异常

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

你可以像这样抛出异常:

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

3
投票

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

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)
}

2
投票

添加上面的所有答案,如果您想要一个错误层次结构,抽象类会有所帮助。

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 并在应用程序/控制器边界上解构(模式匹配)它。

© www.soinside.com 2019 - 2024. All rights reserved.