Dans le cadre de l'écriture d'un itérateur, je me suis retrouvé à écrire le code suivant (suppression de la gestion des erreurs)
public T next() {
try {
return next;
} finally {
next = fetcher.fetchNext(next);
}
}
le trouvant un peu plus facile à lire que
public T next() {
T tmp = next;
next = fetcher.fetchNext(next);
return tmp;
}
Je sais que c'est un exemple simple, où la différence de lisibilité n'est peut-être pas si écrasante, mais je suis intéressé par l'opinion générale quant à savoir s'il est mauvais d'utiliser try-finally dans des cas comme celui-ci où il n'y a pas d'exceptions impliquées, ou s'il est réellement préféré lorsqu'il simplifie le code.
Si c'est mauvais: pourquoi? Style, performance, pièges, ...?
Conclusion Merci toutes vos réponses! Je suppose que la conclusion (au moins pour moi) est que le premier exemple aurait pu être plus lisible s'il s'agissait d'un modèle courant, mais qu'il ne l'est pas. Par conséquent, la confusion introduite par l'utilisation d'une construction en dehors de son objectif, ainsi que le flux d'exceptions éventuellement obscurci, l'emporteront sur toutes les simplifications.
la source
finally
blocs.Iterator
, où vous avez en effet besoin d'une sorte de prélecture pourhasNext()
fonctionner. Essayez-le vous-même.Iterator
est que vous devez récupérer la valeur danshasNext()
(car la chercher est souvent le seul moyen de savoir si elle existe) et la renvoyernext()
comme l'OP l'a fait.Réponses:
Personnellement, je réfléchirais à l'idée de la séparation des requêtes de commande . Lorsque vous y arrivez, next () a deux objectifs: l'objectif annoncé de récupérer l'élément next et l'effet secondaire caché de la mutation de l'état interne de next. Donc, ce que vous faites, c'est atteindre l'objectif annoncé dans le corps de la méthode, puis s'attaquer à un effet secondaire caché dans une clause finalement, ce qui semble ... maladroit, mais pas `` faux '', exactement.
Ce qui se résume vraiment à la façon dont le code est compréhensible, je dirais, et dans ce cas, la réponse est "meh". Vous "essayez" une simple déclaration de retour, puis vous exécutez un effet secondaire caché dans un bloc de code qui est censé être pour la récupération d'erreur à sécurité intégrée. Ce que vous faites est «intelligent», et le code «intelligent» incite souvent les mainteneurs à marmonner «ce que le… oh, je pense que je comprends. Il vaut mieux que les gens lisant votre code marmonnent "oui, euh-huh, c'est logique, oui ..."
Et si vous sépariez la mutation d'état des appels des accesseurs? J'imagine que le problème de lisibilité qui vous préoccupe devient théorique, mais je ne sais pas comment cela affecte votre abstraction plus large. Quelque chose à considérer, de toute façon.
la source
Purement du point de vue du style, je pense que ces trois lignes:
... sont à la fois plus évidents et plus courts qu'un bloc try / finally. Comme vous ne vous attendez pas à ce que des exceptions soient levées, l'utilisation d'un
try
bloc va juste confondre les gens.la source
Cela dépendrait du but du code à l'intérieur du bloc finally. L'exemple canonique est la fermeture d'un flux après sa lecture / écriture, une sorte de "nettoyage" qui doit toujours être fait. La restauration d'un objet (dans ce cas, un itérateur) dans un état valide à mon humble avis compte également comme un nettoyage, donc je ne vois aucun problème ici. Si OTOH que vous utilisiez
return
dès que votre valeur de retour a été trouvée et que vous ajoutiez beaucoup de code non lié dans le bloc finally, cela obscurcirait le but et le rendrait moins compréhensible.Je ne vois aucun problème à l'utiliser quand "il n'y a pas d'exceptions impliquées". Il est très courant de l'utiliser
try...finally
sans uncatch
lorsque le code ne peut que lancerRuntimeException
et que vous ne prévoyez pas de les gérer. Parfois, le n'est finalement qu'une sauvegarde, et vous savez pour la logique de votre programme qu'aucune exception ne sera jamais levée (la condition classique "cela ne devrait jamais arriver").Pièges: toute exception levée à l'intérieur du
try
bloc ferafinally
courir le bock. Cela peut vous mettre dans un état incohérent. Donc, si votre déclaration de retour était quelque chose comme:et ce code a soulevé une exception,
fetchNext
serait toujours exécuté. OTOH si vous l'avez codé comme:alors il ne fonctionnerait pas. Je sais que vous supposez que le code try ne peut jamais déclencher d'exception, mais pour les cas plus complexes que celui-ci, comment pouvez-vous en être sûr? Si votre itérateur était un objet à longue durée de vie, cela continuerait d'exister même si une erreur irrécupérable s'est produite dans le thread actuel, il serait important de le garder dans un état valide à tout moment. Sinon, peu importe vraiment ...
Performances: il serait intéressant de décompiler un tel code pour voir comment il fonctionne sous le capot, mais je ne connais pas assez la JVM pour même faire une bonne supposition ... Les exceptions sont généralement "exceptionnelles", donc le code qui gère ils n'ont pas besoin d'être optimisés pour la vitesse (d'où le conseil de ne jamais utiliser d'exceptions dans le flux de contrôle normal de vos programmes), mais je ne sais pas
finally
.la source
finally
de cette façon? Je veux dire, comme vous le dites, le code finit par avoir des conséquences indésirables; pire encore, vous ne pensez probablement même pas aux exceptions.null
par un code buggy; essayer de lire à partir de lui lèvera une exception, essayer de le fermer dans lefinally
bloc lèvera une autre exception. En outre, il s'agit d'une situation où l'état du calcul n'a pas d'importance, vous souhaitez fermer le flux, qu'une exception se soit produite ou non. Il pourrait également y avoir d'autres situations de ce genre. Pour cette raison, je le traite comme un piège pour les utilisations valables, plutôt que comme une recommandation de ne jamais l'utiliser.L'énoncé de titre: "... enfin clause pour effectuer le travail après le retour ..." est faux. Le bloc finalement se produit avant le retour de la fonction. C'est tout l'intérêt de finalement en fait.
Ce que vous abusez ici, c'est l'ordre d'évaluation, où la valeur de next est stockée pour être retournée avant de la muter. Ce n'est pas une pratique courante et à mon avis, c'est faux car cela vous rend le code non séquentiel et donc beaucoup plus difficile à suivre.
L'utilisation prévue des blocs finally est pour la gestion des exceptions où certaines conséquences doivent se produire indépendamment des exceptions levées, par exemple le nettoyage de certaines ressources, la fermeture de la connexion DB, la fermeture d'un fichier / socket, etc.
la source
Je serais très prudent, car (en général, pas dans votre exemple), une partie dans try peut lever l'exception, et si elle est jetée, rien ne sera retourné et si vous avez réellement l'intention d'exécuter ce qui se trouve finalement après le retour, cela exécuter quand vous ne le vouliez pas.
Et un autre bidon de ver est, qu'en général, une action utile peut finalement lever des exceptions, donc vous pouvez terminer avec une méthode vraiment compliquée.
À cause de cela, quelqu'un qui le lit doit penser à ces problèmes, donc c'est plus difficile à comprendre.
la source