Custom Exception in scala
In order to reflect all the original constructors from Exception I'd implement a custom exception with the following pattern:
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)
}
}
This can be also achieved with a trait as mentioned in previous answer. I'd just not create individual classes in this case:
trait SomeException { self: Throwable =>
def someDetail: SomeDetail
}
then, when throwing:
throw new Exception(...) with SomeException {
override val someDetail = ...
}
and when matching:
try {
...
} catch {
case ex: Throwable with SomeException =>
ex.getCause
ex.getMessage
ex.someDetail
}
The advantage here is that you are not sticking to any particular constructor of the parent exception.
something more or less like that.
final case class CustomException(private val message: String = "",
private val cause: Throwable = None.orNull)
extends Exception(message, cause)
Just try catch:
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)
}
}
This is almost identical to @Jacek L.'s answer. I just wanted to add some more input on the motive behind this answer.
Why so many constructors?
Throwable
is written in kind of a funny way. It has 4 constructors
-- ignoring the one with the boolean
toggles -- each of them behaves a bit differently with null
s, and these differences could only be maintained with multiple constructors.
It would have been a bit cleaner if Scala would have allowed to call a superclass constructor via super
, but it doesn't :(
Why not a case class?
- Perfectly maintaining the constructors' behavior regarding
null
s wouldn't be possible; specifically, bothdef this()
anddef this(message: String)
will have to set thecause
tonull
, while originally it is set tothis
. toString
will not be overridden.- The message and the cause are already publicly available via
getMessage
andgetCause
. Adding another reference to these is redundant. equals
will be overridden and will behave differently.
Meaning,new Exception("m") == new Exception("m") // false
whilenew CaseException("m") == new CaseException("m") // true
If one desires to access the message and the cause via pattern-matching, one can simply implement the unapply
method:
object MyException {
def unapply(e: MyException): Option[(String,Throwable)] = Some((e.getMessage, e.getCause))
}
You might want to create a sealed trait:
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
}
Then you can have as many case class
es as you need extending not only Exception
, but your new trait.
case class CustomException(message: String) extends Exception(message) with MyException {
override val details: JsValue = Json.obj("message" -> message, "etc" -> "Anything else")
}
Now, the whole point of using Scala is walking towards a more functional programming style, it will make your app more concurrent, so if you need to use your new custom exception, you might want to try something like this:
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)
}