Scala拡張Exception
クラスでカスタム例外を作成し、例外が発生したときにそれらをスローすると同時にキャッチする方法はありますか。
Javaの例:
class CustomException extends Exception {
public final static String _FAIL_TO_INSERT = "FAIL_TO_INSERT";
}
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
}
_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つのコンストラクターがあり、それぞれがnull
sとは少し異なる動作をし、これらの違いは複数のコンストラクターでのみ維持できます。
Scalaがsuper
を介してスーパークラスコンストラクターを呼び出すことを許可した場合、それは少しきれいになりましたが、それはしません:(
ケースクラスではない理由
null
sに関するコンストラクターの動作を完全に維持することはできません。具体的には、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))
}
_
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
}
ここでの利点は、親例外の特定のコンストラクターに固執しないことです。
多かれ少なかれそのようなもの。
封印された特性を作成することもできます。
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 class
es 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)
}
このようにカスタム例外を定義します
case class CustomException(s: String) extends Exception(s)
そして、あなたはこのようにあなたの例外を投げることができます:
try{
...
} catch{
case x:Exception => throw new CustomException("whatever")
}
上記のすべての答えに加えて、エラー階層が必要な場合は、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をどこでも使用し、アプリケーション/コントローラーの境界でそれを分解(パターン一致)してください。