L'extension de Throwable en Java
Java vous permet de créer un tout nouveau sous-type de Throwable
, e.g:
public class FlyingPig extends Throwable { ... }
Maintenant, très rarement, je peut faire quelque chose comme ceci:
throw new FlyingPig("Oink!");
et bien sûr ailleurs:
try { ... } catch (FlyingPig porky) { ... }
Mes questions sont:
- Est-ce une mauvaise idée? Et si oui, pourquoi?
- Ce qui aurait pu être fait pour l'empêcher de sous-typage si c'est une mauvaise idée?
- Puisqu'il n'est pas évitable (autant que je sache), ce catastrophes pourrait entraîner?
- Si ce n'est pas une mauvaise idée, pourquoi pas?
- Comment pouvez-vous faire quelque chose d'utile du fait que vous pouvez
extends Throwable
?
- Comment pouvez-vous faire quelque chose d'utile du fait que vous pouvez
Proposé scénario #1
Un scénario où j'ai été vraiment tenté de faire quelque chose comme ce qui a les propriétés suivantes:
- "L'événement" est quelque chose qui sera arriver par la suite. Il est devrait. Le plus n'est certainement pas un
Error
, et il n'y a rienException
-al quand il se produit.- Parce que c'est devrait, il y aura un
catch
en attente pour elle. Il ne sera pas "glisser" dans le passé, rien. Il ne sera pas "s'échapper" de toute tentative decatch
généralException
et/ouError
.
- Parce que c'est devrait, il y aura un
- "L'événement" qui se passe extrêmement rarement.
- Lorsque cela se produit, habituellement, il y a une profonde trace de la pile.
Alors peut-être il est clair maintenant que je suis en train de dire: FlyingPig
est le résultat d'une minutieuse recherche récursive.
L'objet à rechercher existe: c'est seulement une question de le trouver dans la grande mer, qui est l'espace de recherche. Le processus de recherche sera longue, de sorte que le coût relativement élevé de la gestion des exceptions est négligeable. En fait, le traditionnel contrôle de flux de construire une autre de l'utilisation d'un boolean isFound
drapeau peut être plus cher, car il faut contrôler en permanence tout au long du processus de recherche, le plus souvent, à chaque niveau de la récursivité. Cette vérification échoue à 99,99% du temps, mais c'est absolument nécessaire pour propager la résiliation condition. Dans un sens, tandis que efficace, la vérification est inefficace!
Simplement throw
-ing un FlyingPig
lorsque l'cherché objet est trouvé, vous n'avez pas à encombrer le code avec la gestion de la boolean isFound
drapeau. Non seulement le code plus propre à cet égard, mais il peut courir plus vite en raison de cette omission.
Donc, pour résumer, le choix est entre ces deux:
- Traditionnelles de contrôle des flux de
- Utiliser un
boolean isFound
, contrôler en permanence - 99,99% du temps, la vérification est un "déchet", car il faudrait toujours être
false
- Quand il devient finalement
true
, vous vous arrêtez recursing et vous devez vous assurer que vous pouvez bien vous détendre à l'appel initial.
- Utiliser un
FlyingPig
approche- Ne vous embêtez pas avec tout
boolean isFound
. - Si trouvé, juste
throw new FlyingPig()
; c'est devrait, alors il y aura unecatch
pour elle. - Pas de gestion de
boolean
drapeau, pas de gaspillage de vérifier si vous avez besoin de continuer, pas de tenue de livres manuelle de se détendre la récursivité, etc.
- Ne vous embêtez pas avec tout
Questions:
- Est cette technique de (ab)à l'aide d'exception est-elle valide? (Est-il un nom pour ça?)
- Si elle est valable, doit
FlyingPig extends Throwable
, ou estException
bien? (même si il n'y a rien d'exceptionnel à ses circonstances?)
- Drôle d'idée, je suis juste à penser, si cela peut être utilisé pour laisser de la JVM de synchroniser les threads. Est attraper throwables asynchrone?
- J'ai utilisé une fois un
class Finished extends Exception
pour exactement la même raison: la Performance de sortir d'une longue recherche récursive. Mais je n'ai jamais justifié cette raison que le fait de mesurer la performance. - Enfin!!! Quelqu'un qui a eu la même idée que j'ai fait! Je devrais probablement prendre une étape supplémentaire et de référence. Sera probablement un jour.
Vous devez vous connecter pour publier un commentaire.
Je dirais que c'est une très mauvaise idée. Beaucoup de code est mis en œuvre sur l'hypothèse que si vous attrapez
Error
etException
vous avez pris toutes les exceptions possibles. Et la plupart des tutoriels et manuels vous diront la même chose. En créant une sous-classe directe deThrowable
vous êtes potentiellement la création de toutes sortes de maintenance et les problèmes d'interopérabilité.Je ne vois pas de bonne raison de prolonger
Throwable
. ÉtendreException
ouRuntimeException
à la place.MODIFIER - En réponse à l'OP est proposé scénario #1.
Exceptions sont un moyen très coûteux de faire face à la "normale" contrôle de flux. Dans certains cas, nous parlons de milliers des instructions supplémentaires exécutés pour créer, lancer et à attraper une exception. Si vous allez à ignorer les idées reçues et d'utiliser les exceptions pour les non-exceptionnel contrôle de flux, utilisez un
Exception
sous-type. En essayant de faire semblant de quelque chose est un "événement" pas une "exception" en déclarant est comme un sous-type deThrowable
ne va pas à atteindre quoi que ce soit.Cependant, c'est une erreur de confondre une exception avec une erreur, erreur, mauvais, peu importe. Et il n'y a rien de mal avec le représentant d'un "événement exceptionnel qui n'est pas une erreur, erreur, mauvais, ou que ce soit" à l'aide d'une sous-classe de
Exception
. La clé est que l'événement doit être exceptionnelle; c'est à dire hors de l'ordinaire, qui arrive très rarement, ...En résumé, un
FlyingPig
ne peut pas être une erreur, mais c'est pas une raison de ne pas le déclarer comme un sous-type deException
.boolean
test peut être plus cher à long terme que d'un traitement d'exception. Comme je l'ai souligné, la recherche est longue: il peut prendre plusieurs minutes, voire quelques heures. Voir aussi mon commentaire sur Pascal réponse.À mon avis, ce n'est pas une bonne idée:
Exception devrait juste pas être utilisé pour le contrôle de flux. Si je devais le nom de cette technique, je dirais que c'est une odeur de code ou un anti-modèle.
Voir aussi:
Il pourrait y avoir des situations où vous voulez attraper
Throwable
non seulement attraperException
mais aussiError
mais c'est rare, d'habitude les gens ne se prennent pasThrowable
. Mais je n'arrive pas à trouver une situation où vous souhaitez jeter une sous-classe deThrowable
.Et je pense aussi que l'extension de
Throwable
ne pas rendre les choses moins "l'exception " -al", ça a l'air pire - qui a complètement défaites à l'intention.Donc pour conclure, si vous voulez vraiment jeter quelque chose, jeter une sous-classe de
Exception
.Voir aussi:
throw
unFlyingPig
, vouscatch
unFlyingPig
. Je n'ai jamais proposé que l'on doitcatch (Throwable t)
(ce qui j'en conviens est une mauvaise idée).catch (Throwable t)
- vous n'avez pas - mais que cela semble être la seule à l'utilisation raisonnable deThrowable
pour moi (la capture il, ne pas le jeter). J'ai mis à jour ma réponse, espérons que les choses sont plus claires.throw new Exception()
est cher. Concernant les références, consultez Java Réglage des Performances par exemple (mais de toute recherche sur "Java+performance+coût+" l'exception trouverez de nombreux d'entre eux).private static final FlyingPig SHARED_EXCEPTION = (FlyingPig) new Flying().fillInStackTrace();
suivie parthrow SHARED_EXCEPTION;
permettrait d'éviter de remplir la trace de la pile, non? (OK, si vous voulez l'exception de l'état, vous aurez besoin d'une "exception de la piscine" et de gérer le multi-threading, mais si vous êtes jeter flying pigs alors vous êtes probablement assez fou pour rien...) @Pascal merci pour la référence.Ici est un blog de John Rose, un HotSpot architecte:
http://blogs.oracle.com/jrose/entry/longjumps_considered_inexpensive
Il s'agit de "abuser" des exceptions pour le contrôle de flux. Légèrement différents cas d'utilisation, mais..
En bref, il fonctionne vraiment bien si vous préallouer/cloner vos exceptions pour éviter les traces de pile en cours de création.
Je pense que cette technique se justifie si l' "cachés" de clients.
C'est à dire, votre FlyingPig ne devrait jamais être en mesure de quitter votre bibliothèque (toutes les méthodes publiques devraient transitivement garantie de ne pas le jeter). Une façon de garantir ce serait de faire une checked Exception.
Je pense que la seule justification de l'extension de Throwable est parce que vous voulez permettre aux gens de passer des rappels qui ont catch(Exception e) clauses , et vous souhaitez que votre raison d'être ignorés par eux. Je peux me l'acheter...
La
org.junit.Test
annotation comprend laNone
classe qui s'étendThrowable
et est utilisé comme valeur par défaut pour leexpected
paramètre annotation.None
pourrait également avoir été déclaré comme une sous-classe deException
. Et certes, ce n'est pas un exemple à suivre dans d'autres situations.Si vous pouvez justifier ce qui définit un FlyingPig hormis les deux exceptions et des Erreurs, de sorte qu'il n'est pas convenable qu'une sous-classe de soit, alors il n'y a rien de fondamentalement mauvais dans sa création.
Le plus gros problème est, je pense, dans la pragmatique du monde, parfois il y a des raisons valables pour attraper java.lang.Exception à la règle. Votre nouveau type de throwable va voler à droite après les blocs try-catch qui avait toutes les attentes raisonnables de la suppression (ou à l'exploitation forestière, de l'emballage, peu importe) tout non-problème fatal.
Sur le revers de la médaille, si vous êtes en train de faire de la maintenance sur un système ancien qui est de l'onujuste titre, la suppression de java.lang.Exception, vous pouvez tricher autour d'elle. (En supposant que le sincère appel pour le temps de fixer correctement est refusé).
Il n'y a rien de mal avec l'extension de Throwable pour être en mesure de jeter (et gérer) personnalisé exceptions. Toutefois, vous devriez garder à l'esprit les points suivants:
Donc, supposons que vous disposez des éléments suivants classes d'exception dans votre base de code:
Et certaines méthodes
Maintenant, vous pourriez avoir un peu de code qui appelle ces méthodes comme ceci:
Noter que lorsque nous appelons
bar()
, il nous suffit de les attraperPig
, puisque les deuxFlyingPig
etSwine
étendrePig
. Ceci est utile si vous voulez faire la même chose pour traiter une exception. Vous pouvez, cependant, de les gérer différemment:La Jouer! cadre utilise quelque chose comme cela pour le traitement de la demande. Le traitement de la requête passe par de nombreuses couches (routage, de middleware, de contrôleurs, d'un modèle de rendu) et à la couche finale du rendu HTML est enveloppé dans un throwable et jeté, dont la première couche de captures, déballe et l'envoie au client. Si aucune de ces méthodes dans les nombreuses couches concernées doivent renvoyer explicitement un objet de réponse, ni ont-ils besoin d'avoir une réponse de l'objet passé en argument à la propagation et à modifier.
Je suis un peu discret sur les détails. Vous pouvez regarder à travers le code de Jeu! cadre pour plus de détails.