Une question assez stupide. Compte tenu du code:
public static int sum(String a, String b) /* throws? WHAT? */ {
int x = Integer.parseInt(a); // throws NumberFormatException
int y = Integer.parseInt(b); // throws NumberFormatException
return x + y;
}
Pourriez-vous dire si c'est du bon Java ou pas? Ce dont je parle, NumberFormatException
c'est une exception non contrôlée. Vous n'avez pas à le spécifier dans le cadre de la sum()
signature. De plus, pour autant que je sache, l'idée d'exceptions non contrôlées est juste de signaler que l'implémentation du programme est incorrecte, et plus encore, attraper des exceptions non contrôlées est une mauvaise idée, car c'est comme réparer un mauvais programme à l'exécution .
Quelqu'un pourrait-il préciser si:
- Je devrais spécifier
NumberFormatException
dans le cadre de la signature de la méthode. - Je devrais définir ma propre exception vérifiée (
BadDataException
), gérer l'NumberFormatException
intérieur de la méthode et la relancer commeBadDataException
. - Je devrais définir ma propre exception vérifiée (
BadDataException
), valider les deux chaînes comme des expressions régulières et lancer myBadDataException
si elle ne correspond pas. - Ton idée?
Mettre à jour :
Imaginez, ce n'est pas un framework open-source, que vous devriez utiliser pour une raison quelconque. Vous regardez la signature de la méthode et pensez - "OK, ça ne jette jamais". Puis, un jour, vous avez une exception. Est-ce normal?
Mise à jour 2 :
Il y a des commentaires disant que ma sum(String, String)
conception est mauvaise. Je suis tout à fait d'accord, mais pour ceux qui pensent que le problème d'origine n'apparaîtrait jamais si nous avions un bon design, voici une question supplémentaire:
La définition du problème est la suivante: vous avez une source de données où les nombres sont stockés en tant que String
s. Cette source peut être un fichier XML, une page Web, une fenêtre de bureau avec 2 zones d'édition, peu importe.
Votre objectif est d'implémenter la logique qui prend ces 2 String
s, les convertit en int
s et affiche une boîte de message disant "la somme est xxx".
Quelle que soit l'approche que vous utilisez pour concevoir / implémenter cela, vous aurez ces 2 points de fonctionnalité interne :
- Un lieu où vous convertissez
String
àint
- Un endroit où vous ajoutez 2
int
s
La principale question de mon message d'origine est:
Integer.parseInt()
s'attend à ce que la chaîne correcte soit transmise. Chaque fois que vous passez une mauvaise chaîne , cela signifie que votre programme est incorrect (et non " votre utilisateur est un idiot"). Vous devez implémenter le morceau de code où d'une part vous avez Integer.parseInt () avec la sémantique MUST et d'autre part vous devez être OK avec les cas où l'entrée est incorrecte - DEVRAIT la sémantique .
Donc, brièvement: comment implémenter la sémantique DEVRAIT si je n'ai que des bibliothèques MUST .
Réponses:
C'est une bonne question. J'aimerais que plus de gens pensent à de telles choses.
À mon humble avis, lancer des exceptions non vérifiées est acceptable si vous avez passé des paramètres de déchets.
De manière générale, vous ne devriez pas lancer
BadDataException
parce que vous ne devriez pas utiliser les exceptions pour contrôler le flux du programme. Les exceptions sont pour l'exceptionnel. Les appelants de votre méthode peuvent savoir avant de l' appeler si leurs chaînes sont des nombres ou non, il est donc évitable de passer des ordures et peut donc être considéré comme une erreur de programmation , ce qui signifie qu'il est acceptable de lever des exceptions non vérifiées.En ce qui concerne la déclaration
throws NumberFormatException
, ce n'est pas très utile, car peu de gens le remarqueront en raison de la décocher de NumberFormatException. Cependant, les IDE peuvent s'en servir et proposer de s'enroulertry/catch
correctement. Une bonne option est d'utiliser également javadoc, par exemple:/** * Adds two string numbers * @param a * @param b * @return * @throws NumberFormatException if either of a or b is not an integer */ public static int sum(String a, String b) throws NumberFormatException { int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
EDITED :
Les commentateurs ont fait valoir des points valables. Vous devez considérer comment cela sera utilisé et la conception générale de votre application.
Si la méthode sera utilisée partout et qu'il est important que tous les appelants gèrent les problèmes, déclarez la méthode comme lançant une exception vérifiée (obligeant les appelants à gérer les problèmes), mais encombrant le code avec des
try/catch
blocs.Si, d'un autre côté, nous utilisons cette méthode avec des données en qui nous avons confiance, déclarez-la comme ci-dessus, car elle ne devrait jamais exploser et vous évitez l'encombrement du code de
try/catch
blocs essentiellement inutiles .la source
RuntimeException
être manipulé? Doit-il être l'appelant ou doit-on laisser l'exception flotter? Si oui, comment devrait-il être géré?Exceptions
partir des couches inférieures et à les envelopper dans des exceptions de niveau supérieur qui sont plus significatives pour l'utilisateur final et la seconde consiste à utiliser un gestionnaire d'exceptions non interceptées qui peut être initialisé dans le lanceur d'applications.throws
clause est un ajout intéressant, mais pas obligatoire.À mon avis, il serait préférable de gérer la logique d'exception aussi loin que possible. Par conséquent, je préférerais la signature
public static int sum(int a, int b);
Avec votre signature de méthode, je ne changerais rien. Soit tu es
Par conséquent, la gestion des exceptions dans ce cas devient un problème de documentation .
la source
sum
fonction qui attend deux nombres, devrait avoir deux nombres. La façon dont vous les avez obtenus n'est pas liée à lasum
fonction. Séparez correctement votre logique . Donc, je relierais cela à un problème de conception.Numéro 4. Comme indiqué, cette méthode ne doit pas prendre de chaînes comme paramètres, elle doit prendre des entiers. Dans ce cas (puisque java s'enroule au lieu de déborder), il n'y a aucune possibilité d'exception.
est beaucoup plus clair quant à ce que cela signifie que
x = sum(a, b)
Vous voulez que l'exception se produise aussi près que possible de la source (entrée).
En ce qui concerne les options 1 à 3, vous ne définissez pas d'exception car vous vous attendez à ce que vos appelants supposent que sinon votre code ne peut pas échouer, vous définissez une exception pour définir ce qui se passe dans des conditions d'échec connues QUI SONT UNIQUES À VOTRE MÉTHODE. C'est-à-dire si vous avez une méthode qui est un wrapper autour d'un autre objet et qu'elle lève une exception, puis transmettez-la. Ce n'est que si l'exception est unique à votre méthode que vous devriez lancer une exception personnalisée (frex, dans votre exemple, si sum était censé renvoyer uniquement des résultats positifs, alors vérifier cela et lancer une exception serait approprié, si d'un autre côté java a jeté une exception de débordement au lieu d'envelopper, alors vous la transmettriez, vous ne la définiriez pas dans votre signature, la renommez ou la mangeriez).
Mise à jour en réponse à la mise à jour de la question:
La solution à cela est d'encapsuler la bibliothèque MUST et de renvoyer une valeur DEVRAIT. Dans ce cas, une fonction qui renvoie un Integer. Écrivez une fonction qui prend une chaîne et retourne un objet Integer - soit cela fonctionne, soit il renvoie null (comme Ints.tryParse de goyave). Faites votre validation séparément de votre opération, votre opération devrait prendre des ints. Que votre opération soit appelée avec des valeurs par défaut lorsque vous avez une entrée non valide, ou que vous fassiez autre chose, dépendra de vos spécifications - le plus que je puisse dire à ce sujet, c'est qu'il est vraiment improbable que l'endroit où prendre cette décision se trouve dans l'opération. méthode.
la source
sum(int, int)
etparseIntFromString(String)
. Pour obtenir la même fonctionnalité, dans les deux cas, je vais devoir écrire un morceau de code où il y a 2 appels àparseIntFromString()
et 1 appel àsum()
. Considérez ce cas, si cela a du sens pour vous - je ne vois aucune différence du point de vue du problème d'origine.int sum(String, String)
n'est jamais possible en réalité?Je le pense. C'est une belle documentation.
Parfois oui. Les exceptions vérifiées sont considérées comme meilleures dans certains cas, mais travailler avec elles est tout à fait un PITA. C'est pourquoi de nombreux frameworks (par exemple, Hibernate) n'utilisent que des exceptions d'exécution.
Jamais. Plus de travail, moins de vitesse (sauf si vous vous attendez à ce que l'exception soit une règle), et aucun gain du tout.
Pas du tout.
la source
N ° 4.
Je pense que je ne changerais pas du tout la méthode. Je mettrais un essai autour de la méthode d'appel ou plus haut dans la pile-trace où je suis dans un contexte où je peux récupérer gracieusement avec la logique métier de l'exception.
Je ne ferais pas avec certitude le numéro 3 car je le juge excessif.
la source
En supposant que ce que vous écrivez va être consommé (comme une API) par quelqu'un d'autre, alors vous devriez aller avec 1, NumberFormatException est spécifiquement dans le but de communiquer de telles exceptions et devrait être utilisé.
la source
Vous devez d'abord vous demander si l'utilisateur de ma méthode doit s'inquiéter de la saisie de données erronées ou est-il attendu de lui qu'il saisisse les données appropriées (dans ce cas, String). Cette attente est également connue sous le nom de conception par contrat .
et 3. Oui, vous devriez probablement définir BadDataException ou même mieux utiliser certains des excisants comme NumberFormatException mais plutôt laisser le message standard à afficher. Attrapez NumberFormatException dans la méthode et relancez-la avec votre message, sans oublier d'inclure la trace de pile d'origine.
Cela dépend de la situation, mais j'irais probablement de nouveau avec NumberFormatException avec quelques informations supplémentaires. Et aussi il doit y avoir une explication javadoc de ce que sont les valeurs attendues pour
String a, String b
la source
Cela dépend beaucoup du scénario dans lequel vous vous trouvez.
Cas 1. C'est toujours vous qui déboguez le code et personne d'autre et l'exception ne causera pas une mauvaise expérience utilisateur
Lancez le NumberFormatException par défaut
Cas 2: le code doit être extrêmement maintenable et compréhensible
Définissez votre propre exception et ajoutez beaucoup plus de données pour le débogage tout en la lançant.
Vous n'avez pas besoin de vérifications de regex car, de toute façon, cela ira à l'exception sur une mauvaise entrée.
S'il s'agissait d'un code de niveau production, mon idée serait de définir plus d'une exception personnalisée, comme
et gérer tout cela séparément
la source
Dans l'ensemble, une NumberFormaException n'est pas cochée car on s'attend à ce qu'une entrée correctement analysable soit fournie. La validation d'entrée est quelque chose que vous devez gérer. Cependant, l'analyse de l'entrée est le moyen le plus simple de le faire. Vous pouvez simplement laisser votre méthode telle quelle et avertir dans la documentation qu'une entrée correcte est attendue et que toute personne appelant votre fonction doit valider les deux entrées avant de l'utiliser.
la source
Tout comportement exceptionnel doit être clarifié dans la documentation. Soit il doit indiquer que cette méthode renvoie une valeur spéciale en cas d'échec (comme
null
, en changeant le type de retour enInteger
), soit le cas 1 doit être utilisé. Son explicite dans la signature de la méthode permet à l'utilisateur de l'ignorer s'il garantit des chaînes correctes par d'autres moyens, mais il est toujours évident que la méthode ne gère pas ce type d'échec par elle-même.la source
Répondez à votre question mise à jour.
Oui, il est parfaitement normal d'avoir des exceptions "surprises". Pensez à toutes les erreurs d'exécution que vous rencontrez lorsque vous débutez en programmation.
Également une exception de surprise courante pour chaque boucle.
la source
Bien que je sois d'accord avec la réponse selon laquelle l'exception d'exécution devrait être autorisée à être percolée, du point de vue de la conception et de la convivialité, ce serait une bonne idée de l'envelopper dans une IllegalArgumentException plutôt que de la lancer comme NumberFormatException. Cela rend alors le contrat de votre méthode plus clair par lequel il déclare qu'un argument non conforme lui a été passé en raison duquel il a levé une exception.
En ce qui concerne la mise à jour de la question "Imaginez, ce n'est pas un framework open-source, que vous devriez utiliser pour une raison quelconque. Vous regardez la signature de la méthode et pensez -" OK, ça ne lève jamais ". Puis, un jour, vous avez une exception . Est-ce normal?" le javadoc de votre méthode doit toujours déborder le comportement de votre méthode (pré et post contraintes). Pensez aux lignes des interfaces de collecte, par exemple, où si un null n'est pas autorisé, le javadoc dit qu'une exception de pointeur nul sera levée bien qu'elle ne fasse jamais partie de la signature de la méthode.
la source
Comme vous parlez de bonnes pratiques Java, à mon avis, c'est toujours mieux
Pour gérer l'exception non cochée, analysez-la et par le biais d'une exception non cochée personnalisée.
De plus, tout en lançant une exception non vérifiée personnalisée, vous pouvez ajouter le message d'exception que votre client pourrait comprendre et également imprimer la trace de la pile de l'exception d'origine
Pas besoin de déclarer une exception personnalisée comme "jette" car elle est décochée.
De cette façon, vous ne violez pas l'utilisation de ce pour quoi les exceptions non vérifiées sont faites, en même temps le client du code comprendrait facilement la raison et la solution de l'exception.
Documenter correctement en java-doc est également une bonne pratique et aide beaucoup.
la source
Je pense que cela dépend de votre objectif, mais je le documenterais au minimum:
/** * @return the sum (as an int) of the two strings * @throws NumberFormatException if either string can't be converted to an Integer */ public static int sum(String a, String b) int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
Ou, prenez une page du code source Java pour la classe java.lang.Integer:
public static int parseInt(java.lang.String string) throws java.lang.NumberFormatException;
la source
Qu'en est-il du modèle de validation d'entrée implémenté par la bibliothèque «Guava» de Google ou la bibliothèque «Validator» d'Apache ( comparaison )?
D'après mon expérience, il est considéré comme une bonne pratique de valider les paramètres d'une fonction au début de la fonction et de lancer des exceptions le cas échéant.
De plus, je considère que cette question est largement indépendante de la langue. La «bonne pratique» s'appliquerait ici à tous les langages qui ont des fonctions qui peuvent accepter des paramètres valides ou non.
la source
Je pense que votre toute première phrase «Une question assez stupide» est très pertinente. Pourquoi écririez-vous une méthode avec cette signature en premier lieu? Est-il même judicieux de faire la somme de deux chaînes? Si la méthode appelante veut additionner deux chaînes, il est de la responsabilité de la méthode appelante de s'assurer que ce sont des entiers valides et de les convertir avant d'appeler la méthode.
Dans cet exemple, si la méthode appelante ne peut pas convertir les deux chaînes en un entier, elle pourrait faire plusieurs choses. Cela dépend vraiment de la couche à laquelle cette sommation se produit. Je suppose que la conversion de chaîne serait très proche du code frontal (si cela était fait correctement), de sorte que le cas 1. serait le plus probable:
la source
Il y a beaucoup de réponses intéressantes à cette question. Mais je veux toujours ajouter ceci:
Pour l'analyse des chaînes, je préfère toujours utiliser des "expressions régulières". Le package java.util.regex est là pour nous aider. Je vais donc finir avec quelque chose comme ça, qui ne lève jamais d'exception. C'est à moi de renvoyer une valeur spéciale si je veux attraper une erreur:
import java.util.regex.Pattern; import java.util.regex.Matcher; public static int sum(String a, String b) { final String REGEX = "\\d"; // a single digit Pattern pattern = Pattern.compile(REGEX); Matcher matcher = pattern.matcher(a); if (matcher.find()) { x = Integer.matcher.group(); } Matcher matcher = pattern.matcher(b); if (matcher.find()) { y = Integer.matcher.group(); } return x + y; }
Comme on peut le voir, le code est juste un peu plus long, mais nous pouvons gérer ce que nous voulons (et définir des valeurs par défaut pour x et y, contrôler ce qui se passe avec les clauses else, etc ...) Nous pourrions même écrire une transformation plus générale routine, à laquelle nous pouvons passer des chaînes, valeurs de retour par défaut, code REGEX à compiler, messages d'erreur à lancer, ...
J'espère que c'était utile.
Attention: je n'ai pas pu tester ce code, veuillez donc excuser d'éventuels problèmes de syntaxe.
la source
Integer.matcher
?private
variable à l'intérieur de la méthode? Manquant()
pour ifs, manque beaucoup de;
,x
,y
non déclaré, amatcher
déclaré deux fois, ...Integer.matcher.group()
soit censé êtreInteger.parseInt(matcher.group())
)Vous êtes confronté à ce problème parce que vous laissez les erreurs des utilisateurs se propager trop profondément dans le cœur de l'application et en partie aussi parce que vous abusez des types de données Java.
Vous devriez avoir une séparation plus claire entre la validation des entrées utilisateur et la logique métier, utiliser un typage de données approprié, et ce problème disparaîtra de lui-même.
Le fait est que la sémantique de
Integer.parseInt()
sont connues - son objectif principal est d'analyser les entiers valides . Il vous manque une étape de validation / analyse explicite des entrées utilisateur.la source