Je ne peux pas comprendre où le final
mot-clé est vraiment pratique lorsqu'il est utilisé sur des paramètres de méthode.
Si nous excluons l'utilisation de classes anonymes, la lisibilité et la déclaration d'intention, cela me semble presque sans valeur.
Faire en sorte que certaines données restent constantes n'est pas aussi solide qu'il y paraît.
Si le paramètre est une primitive, il n'aura aucun effet puisque le paramètre est transmis à la méthode en tant que valeur et sa modification n'aura aucun effet en dehors de la portée.
Si nous transmettons un paramètre par référence, alors la référence elle-même est une variable locale et si la référence est modifiée de l'intérieur de la méthode, cela n'aurait aucun effet de l'extérieur de la portée de la méthode.
Considérez l'exemple de test simple ci-dessous. Ce test réussit bien que la méthode ait changé la valeur de la référence qui lui a été donnée, il n'a aucun effet.
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
la source
int foo(int* bar)
et le dernierint foo(int* &bar)
. Ce dernier passe un pointeur par référence, le premier passe une référence par valeur.Réponses:
Arrêter la réaffectation d'une variable
Bien que ces réponses soient intellectuellement intéressantes, je n'ai pas lu la réponse simple et courte:
Que la variable soit une variable statique, une variable membre, une variable locale ou une variable argument / paramètre, l'effet est entièrement le même.
Exemple
Voyons l'effet en action.
Considérez cette méthode simple, où les deux variables ( arg et x ) peuvent toutes deux être réaffectées à des objets différents.
Marquez la variable locale comme finale . Il en résulte une erreur de compilation.
Au lieu de cela, marquons la variable de paramètre comme finale . Cela entraîne également une erreur de compilation.
Morale de l'histoire:
Ne jamais réaffecter d'arguments
Comme bonne pratique de programmation (dans n'importe quel langage), vous ne devez jamais réaffecter une variable de paramètre / argument à un objet autre que l'objet passé par la méthode appelante. Dans les exemples ci-dessus, il ne faut jamais écrire la ligne
arg =
. Puisque les humains font des erreurs et que les programmeurs sont humains, demandons au compilateur de nous aider. Marquez chaque variable de paramètre / argument comme «finale» afin que le compilateur puisse trouver et marquer de telles réaffectations.En rétrospective
Comme indiqué dans d'autres réponses… Étant donné l'objectif de conception original de Java d'aider les programmeurs à éviter les erreurs stupides telles que la lecture après la fin d'un tableau, Java aurait dû être conçu pour appliquer automatiquement toutes les variables de paramètre / argument comme «finales». En d'autres termes, les arguments ne doivent pas être des variables . Mais le recul est une vision de 20/20, et les concepteurs Java avaient les mains pleines à l'époque.
Alors, toujours ajouter
final
à tous les arguments?Devrions-nous ajouter
final
à chaque paramètre de méthode déclaré?➥ Ajouter
final
uniquement lorsque le code de la méthode est long ou compliqué, où l'argument peut être confondu avec une variable locale ou membre et éventuellement réaffecté.Si vous achetez dans la pratique de ne jamais réattribuer un argument, vous serez enclin à ajouter un
final
à chacun. Mais cela est fastidieux et rend la déclaration un peu plus difficile à lire.Pour un code simple court où l'argument est évidemment un argument, et non une variable locale ni une variable membre, je ne me donne pas la peine d'ajouter le
final
. Si le code est assez évident, sans aucune chance pour moi ni pour aucun autre programmeur de faire de la maintenance ou de refactoriser par erreur la variable argument comme autre chose qu'un argument, alors ne vous embêtez pas. Dans mon propre travail, j'ajoutefinal
uniquement du code plus long ou plus impliqué où un argument peut être confondu avec une variable locale ou membre.Un autre cas ajouté pour l'exhaustivité
la source
message = ( msg || 'Hello World"' )
. Il n'y a tout simplement aucune raison de ne pas utiliser une var distincte. Le seul coût est de quelques octets de mémoire.message = ( msg || 'Hello World"' )
me risque plus tard d'utiliser accidentellementmsg
. Lorsque le contrat que j'entends est «un comportement sans arg / nul / non défini ne peut être ignoré"Hello World"
», c'est une bonne pratique de programmation de s'y engager tôt dans la fonction. [Cela peut être réalisé sans réaffectation en commençant parif (!msg) return myfunc("Hello World");
mais cela devient difficile à utiliser avec plusieurs arguments.] Dans les rares cas où la logique de la fonction devrait se soucier de savoir si la valeur par défaut a été utilisée, je préfère désigner une valeur sentinelle spéciale (de préférence publique) .message
etmsg
. Mais s'il l'appelle quelque chose commeprocessedMsg
ou quelque chose d'autre qui fournit un contexte supplémentaire - le risque d'erreur est beaucoup plus faible. Concentrez-vous sur ce qu'il dit et non sur «comment» il le dit. ;)Parfois, il est agréable d'être explicite (pour plus de lisibilité) que la variable ne change pas. Voici un exemple simple où l'utilisation
final
peut sauver certains maux de tête possibles:Si vous oubliez le mot-clé «this» sur un setter, la variable que vous souhaitez définir ne sera pas définie. Cependant, si vous avez utilisé le
final
mot clé sur le paramètre, le bogue serait détecté au moment de la compilation.la source
Oui, à l'exception des classes anonymes, de la lisibilité et de la déclaration d'intention, c'est presque sans valeur. Ces trois choses sont-elles cependant sans valeur?
Personnellement, j'ai tendance à ne pas utiliser
final
de variables et de paramètres locaux, sauf si j'utilise la variable dans une classe interne anonyme, mais je peux certainement voir l'intérêt de ceux qui veulent préciser que la valeur du paramètre ne changera pas (même si l'objet auquel il se réfère change de contenu). Pour ceux qui trouvent que cela ajoute à la lisibilité, je pense que c'est une chose tout à fait raisonnable à faire.Votre point serait plus important si quelqu'un prétend en fait que ce ne garder constant de données d'une manière qu'il ne fonctionne pas - mais je ne me souviens pas avoir vu de telles réclamations. Suggérez-vous qu'il existe un nombre important de développeurs suggérant que cela
final
a plus d'effet qu'il ne le fait vraiment?EDIT: J'aurais vraiment dû résumer tout cela avec une référence Monty Python; la question semble quelque peu similaire à demander "Qu'est-ce que les Romains ont fait pour nous?"
la source
Permettez-moi d'expliquer un peu le cas où vous devez utiliser final, que Jon a déjà mentionné:
Si vous créez une classe interne anonyme dans votre méthode et utilisez une variable locale (comme un paramètre de méthode) à l'intérieur de cette classe, le compilateur vous oblige à rendre le paramètre final:
Ici, les paramètres
from
etto
doivent être définitifs pour pouvoir être utilisés dans la classe anonyme.La raison de cette exigence est la suivante: les variables locales vivent sur la pile, elles n'existent donc que pendant l'exécution de la méthode. Cependant, l'instance de classe anonyme est renvoyée par la méthode, elle peut donc vivre beaucoup plus longtemps. Vous ne pouvez pas conserver la pile, car elle est nécessaire pour les appels de méthode suivants.
Donc, ce que Java fait à la place, c'est de mettre des copies de ces variables locales en tant que variables d'instance cachées dans la classe anonyme (vous pouvez les voir si vous examinez le code d'octet). Mais s'ils n'étaient pas définitifs, on pourrait s'attendre à ce que la classe anonyme et la méthode voient les changements que l'autre apporte à la variable. Afin de maintenir l'illusion qu'il n'y a qu'une seule variable plutôt que deux copies, elle doit être définitive.
la source
final
paramètre de fonction et un paramètre de fonction non final aux yeux de HotSpot?J'utilise la finale tout le temps sur les paramètres.
Cela ajoute-t-il autant? Pas vraiment.
Vais-je l'éteindre? Non.
La raison: j'ai trouvé 3 bugs où les gens avaient écrit du code bâclé et n'ont pas réussi à définir une variable membre dans les accesseurs. Tous les bogues se sont révélés difficiles à trouver.
J'aimerais voir cela devenir le défaut dans une future version de Java. Le passage par valeur / référence fait monter énormément de programmeurs juniors.
Une dernière chose .. mes méthodes ont tendance à avoir un petit nombre de paramètres, donc le texte supplémentaire sur une déclaration de méthode n'est pas un problème.
la source
L'utilisation de final dans un paramètre de méthode n'a rien à voir avec ce qui arrive à l'argument du côté de l'appelant. Il est uniquement destiné à le marquer comme ne changeant pas à l'intérieur de cette méthode. Alors que j'essaie d'adopter un style de programmation plus fonctionnel, j'en vois la valeur.
la source
final
les paramètres dans les déclarations d'interfaces / méthodes abstraites.Personnellement, je n'utilise pas final sur les paramètres de méthode, car cela ajoute trop d'encombrement aux listes de paramètres. Je préfère imposer que les paramètres de méthode ne soient pas modifiés par quelque chose comme Checkstyle.
Pour les variables locales que j'utilise final dans la mesure du possible, je laisse même Eclipse le faire automatiquement dans ma configuration pour des projets personnels.
Je voudrais certainement quelque chose de plus fort comme C / C ++ const.
la source
Étant donné que Java transmet des copies d'arguments, je pense que la pertinence de
final
est plutôt limitée. Je suppose que l'habitude vient de l'ère C ++ où vous pouviez interdire la modification du contenu de référence en faisant aconst char const *
. Je pense que ce genre de choses vous fait croire que le développeur est intrinsèquement stupide comme f *** et doit être protégé contre vraiment tous les personnages qu'il tape. En toute humilité, je peux dire que j'écris très peu de bogues même si j'en ometfinal
(sauf si je ne veux pas que quelqu'un écrase mes méthodes et mes classes). Peut-être que je suis juste un développeur de la vieille école.la source
Je n'utilise jamais final dans une liste de paramètres, cela ajoute simplement de l'encombrement comme l'ont dit les répondants précédents. Dans Eclipse, vous pouvez également définir l'attribution des paramètres pour générer une erreur, donc l'utilisation de final dans une liste de paramètres me semble assez redondante. Fait intéressant, lorsque j'ai activé le réglage Eclipse pour l'attribution des paramètres, générant une erreur, il a détecté ce code (c'est juste ainsi que je me souviens du flux, pas du code réel.): -
En jouant l'avocat du diable, qu'est-ce qui ne va pas exactement avec ça?
la source
Réponse courte:
final
aide un tout petit peu mais ... utilisez plutôt une programmation défensive côté client.En effet, le problème
final
est qu'il n'impose que la référence est inchangée, permettant joyeusement aux membres de l'objet référencé d'être mutés, à l'insu de l'appelant. Par conséquent, la meilleure pratique à cet égard est la programmation défensive du côté de l'appelant, créant des instances profondément immuables ou des copies profondes d'objets qui risquent d'être agressés par des API sans scrupules.la source
Une raison supplémentaire d'ajouter des déclarations finales aux paramètres est qu'elle permet d'identifier les variables qui doivent être renommées dans le cadre d'une refactorisation de la "méthode d'extraction". J'ai constaté que l'ajout de final à chaque paramètre avant de démarrer une refactorisation de grande méthode me dit rapidement s'il y a des problèmes que je dois résoudre avant de continuer.
Cependant, je les supprime généralement comme superflus à la fin du refactoring.
la source
Suivi de ce post de Michel. J'ai moi-même fait un autre exemple pour l'expliquer. J'espère que ça pourrait aider.
À partir du code ci - dessus, dans la méthode thisIsWhy () , nous avons en fait ne pas assignons le [ l' argument MyObj obj] à une véritable référence en MyParam. À la place, nous utilisons simplement [l'argument MyObj obj] dans la méthode à l'intérieur de MyParam.
Mais après avoir terminé la méthode thisIsWhy () , l'argument (objet) MyObj devrait-il toujours exister?
Il semble que ce soit le cas, parce que nous pouvons voir que nous appelons toujours la méthode showObjName () et qu'elle doit atteindre obj . MyParam utilisera / atteint toujours l'argument de la méthode même si la méthode était déjà de retour!
Comment Java y parvient vraiment est de générer une copie est également une référence cachée de l' argument MyObj obj à l'intérieur de l'objet MyParam (mais ce n'est pas un champ formel dans MyParam afin que nous ne puissions pas le voir)
Comme nous appelons "showObjName", il utilisera cette référence pour obtenir la valeur correspondante.
Mais si nous n'avons pas mis l'argument final, ce qui conduit à une situation, nous pouvons réaffecter une nouvelle mémoire (objet) à l' argument MyObj obj .
Techniquement, il n'y a pas de conflit du tout! Si nous sommes autorisés à le faire, voici la situation:
Pas d'affrontement, mais "CONFUSANT !!" Parce qu'ils utilisent tous le même "nom de référence" qui est "obj" .
Pour éviter cela, définissez-le comme "final" pour éviter que le programmeur ne fasse le code "sujet aux erreurs".
la source