La règle de base est d'utiliser les exceptions pour des cas vraiment exceptionnels**. Pour une défaillance "ordinaire", il est préférable d'utiliser Option
o Either
. Si vous êtes en interface avec Java où des exceptions sont levées lorsque quelqu'un éternue de la mauvaise façon, vous pouvez utiliser Try
pour assurer votre sécurité.
Prenons quelques exemples.
Supposons que vous ayez une méthode qui récupère quelque chose dans une carte. Qu'est-ce qui pourrait mal se passer ? Eh bien, quelque chose de dramatique et de dangereux comme un défaut d'isolement * dépassement de pile, ou quelque chose d'attendu comme l'élément n'est pas trouvé. Vous laisseriez le défaut d'isolement un dépassement de pile lève une exception, mais si vous ne trouvez simplement pas d'élément, pourquoi ne pas renvoyer une valeur Option[V]
au lieu de la valeur ou d'une exception (ou null
) ?
Supposons maintenant que vous écriviez un programme dans lequel l'utilisateur est censé saisir un nom de fichier. Si vous n'avez pas l'intention d'abandonner instantanément le programme en cas de problème, un fichier Either
est la voie à suivre :
def main(args: Array[String]) {
val f = {
if (args.length < 1) Left("No filename given")
else {
val file = new File(args(0))
if (!file.exists) Left("File does not exist: "+args(0))
else Right(file)
}
}
// ...
}
Supposons maintenant que vous souhaitiez analyser une chaîne contenant des nombres délimités par des espaces.
val numbers = "1 2 3 fish 5 6" // Uh-oh
// numbers.split(" ").map(_.toInt) <- will throw exception!
val tried = numbers.split(" ").map(s => Try(s.toInt)) // Caught it!
val good = tried.collect{ case Success(n) => n }
Vous avez donc trois façons (au moins) de faire face à différents types d'échec : Option
pour qu'il ait fonctionné / n'ait pas fonctionné, dans les cas où le fait de ne pas fonctionner est un comportement attendu, et non un échec choquant et alarmant ; Either
pour les cas où les choses peuvent fonctionner ou non (ou, en fait, dans tous les cas où vous avez deux options qui s'excluent mutuellement) et où vous voulez sauvegarder des informations sur ce qui n'a pas fonctionné ; et Try
lorsque vous ne voulez pas vous occuper vous-même de la gestion des exceptions, mais que vous avez quand même besoin de vous interfacer avec du code qui ne connaît pas d'exceptions.
Par ailleurs, les exceptions constituent de bons exemples - c'est pourquoi vous les trouverez plus souvent dans un manuel ou un matériel d'apprentissage qu'ailleurs, je pense : les exemples des manuels sont très souvent incomplets, ce qui signifie que les problèmes graves qui devraient normalement être évités par une conception soignée devraient plutôt être signalés par la levée d'une exception.
* Edit : Les Segfaults plantent la JVM et ne devraient jamais se produire quel que soit le bytecode ; même une exception ne vous aidera pas dans ce cas. Je parlais de débordement de pile.
** Edit : Les exceptions (sans trace de pile) sont également utilisées pour le flux de contrôle dans Scala - c'est en fait un mécanisme assez efficace, et elles permettent des choses comme la définition d'une bibliothèque par l'utilisateur. break
et d'une return
qui revient de votre méthode alors que le contrôle est passé dans une ou plusieurs fermetures. La plupart du temps, vous ne devriez pas vous en préoccuper vous-même, sauf pour réaliser que la capture de tous Throwable
n'est pas une très bonne idée, car vous risquez d'attraper par erreur l'une de ces exceptions relatives au flux de contrôle.