web-dev-qa-db-ja.com

scalaのカスタム例外

Scala拡張Exceptionクラスでカスタム例外を作成し、例外が発生したときにそれらをスローすると同時にキャッチする方法はありますか。

Javaの例:

class CustomException extends Exception {

  public final static String _FAIL_TO_INSERT = "FAIL_TO_INSERT";

}
34
Nilesh
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
}
40
Andrzej Jozwik
_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はおかしな方法で書かれています。 booleanトグルを持つコンストラクターを無視する4つのコンストラクターがあり、それぞれがnullsとは少し異なる動作をし、これらの違いは複数のコンストラクターでのみ維持できます。

Scalaがsuperを介してスーパークラスコンストラクターを呼び出すことを許可した場合、それは少しきれいになりましたが、それはしません:(

ケースクラスではない理由

  • nullsに関するコンストラクターの動作を完全に維持することはできません。具体的には、def this()def this(message: String)の両方でcausenullに設定する必要がありますが、元はthisに設定されています。
  • toStringは上書きされません。
  • メッセージと原因は、getMessagegetCauseを介して既に公開されています。これらに別の参照を追加することは冗長です。
  • equalsはオーバーライドされ、異なる動作をします
    意味、new Exception("m") == new Exception("m") // false
    while new CaseException("m") == new CaseException("m") // true

パターンマッチングを介してメッセージと原因にアクセスしたい場合は、単にunapplyメソッドを実装できます。

_object MyException {
  def unapply(e: MyException): Option[(String,Throwable)] = Some((e.getMessage, e.getCause))
}
_
19
Eyal Roth

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
}

ここでの利点は、親例外の特定のコンストラクターに固執しないことです。

多かれ少なかれそのようなもの。

7
Jacek L.

封印された特性を作成することもできます。

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

次に、case classes Exceptionだけでなく、新しい特性を拡張する必要があるため。

case class CustomeException(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.lenght compareTo 5 match {
                    case 1 => true
                    case _ => false
                }
            case _ => throw CustomeException("Was expecting some txt")
        }
    )
    match {
        case Success(bool) => Future.success(bool)
        case Failure(e) => Future.failed(e)
    }
7
Mesi Rendón

このようにカスタム例外を定義します

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

そして、あなたはこのようにあなたの例外を投げることができます:

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

上記のすべての答えに加えて、エラー階層が必要な場合は、abstract classが役立ちます。

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

抽象クラスの代わりにtraitを使用しても同じことが可能ですが、コンストラクタパラメータがないという点で制限されています。

おそらくGenericErrorをどこでも使用し、アプリケーション/コントローラーの境界でそれを分解(パターン一致)してください。

1
Gagandeep Kalra