web-dev-qa-db-fra.com

Exception personnalisée dans scala

comment puis-je créer des exceptions personnalisées dans Scala étendre la classe Exception et les jeter quand une exception se produit ainsi que les intercepter.

exemple dans 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) 

Juste essayer attraper:

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

Ceci est presque identique à la réponse de @Jacek L. Je voulais juste ajouter quelque chose de plus sur le motif de cette réponse.

Pourquoi tant de constructeurs?

Throwable est écrit de manière amusante. Il a 4 constructeurs - en ignorant celui avec les commutateurs boolean - chacun d'entre eux se comporte un peu différemment avec null s, et ces différences ne pouvaient être maintenues qu'avec plusieurs constructeurs.

Cela aurait été un peu plus propre si Scala aurait permis d'appeler un constructeur de superclasse via super, mais ce n'est pas le cas :(

Pourquoi pas une classe de cas?

  • Maintenir parfaitement le comportement des constructeurs vis-à-vis de null ne serait pas possible; Plus précisément, def this() et def this(message: String) devront définir le paramètre cause sur null, alors qu'il est défini à l'origine sur this.
  • toString ne sera pas remplacé.
  • Le message et la cause sont déjà accessibles au public via getMessage et getCause. Ajouter une autre référence à ceux-ci est redondant.
  • equals sera remplacé et se comportera différemment.
    Signification, new Exception("m") == new Exception("m") // false
    tandis que new CaseException("m") == new CaseException("m") // true

Si l'on souhaite accéder au message et à la cause via un filtrage, il suffit d'appliquer la méthode unapply:

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

Afin de refléter tous les constructeurs d'origine d'Exception, je mettrais en œuvre une exception personnalisée avec le modèle suivant:

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

Ceci peut également être réalisé avec un trait comme mentionné dans la réponse précédente. Je ne créerais pas de cours individuels dans ce cas:

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

puis, en lançant:

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

et lors de l'appariement:

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

L'avantage ici est que vous ne vous en tenez pas à un constructeur particulier de l'exception parente.

quelque chose de plus ou moins comme ça.

7
Jacek L.

Vous voudrez peut-être créer un trait scellé:

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
}

Ensuite, vous pouvez avoir autant de case classes car vous avez besoin d'étendre non seulement Exception mais votre nouveau trait.

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

Maintenant, l’intérêt d’utiliser Scala avance vers un style de programmation plus fonctionnel, cela rendra votre application plus simultanée. Si vous devez utiliser votre nouvelle exception personnalisée, vous voudrez peut-être essayer quelque chose comme ça:

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

Vous définissez votre exception personnalisée comme ceci

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

Et vous pouvez lancer votre exception comme ceci:

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

Ajouter à toutes les réponses ci-dessus, si du tout vous voulez avoir une hiérarchie d'erreur, classe abstraite aiderait.

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

La même chose est possible en utilisant trait au lieu d'une classe abstraite, mais ils sont limités en ce qu'ils n'ont pas de paramètres constructeur.

Utilisez probablement GenericError partout et déconstruisez-le (correspondance de modèle) sur la limite de l'application/du contrôleur.

1
Gagandeep Kalra