454 votes

Pourquoi dois-je utiliser le mot clé "final" sur un paramètre de méthode en Java ?

Je n'arrive pas à comprendre où le final Le mot clé est vraiment pratique lorsqu'il est utilisé sur des paramètres de méthode.

Si l'on exclut l'utilisation de classes anonymes, la lisibilité et la déclaration d'intention, elle me semble presque sans valeur.

Faire en sorte que certaines données restent constantes n'est pas aussi fort 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 que sa modification n'aura aucun effet en dehors de la portée.

  • Si nous passons 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 à l'intérieur de la méthode, cela n'aura aucun effet en dehors de la portée de la méthode.

Considérez l'exemple de test simple ci-dessous. Ce test passe bien que la méthode ait changé la valeur de la référence qui lui a été donnée, elle 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;
}

117 votes

Un petit point sur la terminologie - Java n'a pas du tout de pass-by-reference. Il a une référence de passage par valeur ce qui n'est pas la même chose. Avec une véritable sémantique de passage par référence, les résultats de votre code seraient différents.

10 votes

Quelle est la différence entre le passage par référence et le passage par valeur ?

1 votes

Il est plus facile de décrire cette différence dans un contexte C (du moins pour moi). If I pass a pointer to a method like: <code>int foo(int bar)</code>, alors ce pointeur est transmis par valeur. Cela signifie qu'il est copié, donc si je fais quelque chose à l'intérieur de cette méthode comme <code>free(bar) ; bar = malloc(...);</code> alors je viens de faire une très mauvaise chose. The free call will actually free up the chunk of memory being pointed at (so whatever pointer I passed in is now dangling). However, <code>int foo(int &bar)</bar> signifie que ce code est valide et que la valeur du pointeur transmis sera modifiée.

338voto

Basil Bourque Points 8938

Si ces réponses sont intellectuellement intéressantes, je n'ai pas lu la réponse courte et simple :

Utilisez le mot-clé final lorsque vous voulez que le compilateur empêche une d'être réassignée à un objet différent.

Que la variable soit une variable membre, une variable locale ou une variable argument (paramètre), l'effet est entièrement le même.

Voyons l'effet en action.

Considérons cette méthode simple, où les deux variables ( arg et x ) peuvent tous deux être réaffectés à des objets différents.

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

Marquez la variable locale comme final . Cela entraîne une erreur de compilation.

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

Au lieu de cela, marquons la variable paramètre en tant que final . Cela entraîne également une erreur de compilation.

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

Morale de l'histoire :

Si vous voulez vous assurer qu'une variable pointe toujours vers le même objet, marquez la variable final .

Comme bonne pratique de programmation (dans n'importe quel langage), vous devriez jamais réaffecter un paramètre/une variable d'argument à un objet autre que l'objet transmis 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 paramètre/argument variable comme "final" afin que le compilateur puisse trouver et signaler toute réaffectation de ce type.

Comme indiqué dans d'autres réponses Étant donné que l'objectif initial de Java est d'aider les programmeurs à éviter les erreurs stupides telles que la lecture au-delà de la fin d'un tableau, Java aurait dû être conçu pour imposer automatiquement toutes les variables de paramètres/arguments comme "finales". En d'autres termes, Les arguments ne doivent pas être des variables . Mais le recul est une vision 20/20, et les concepteurs de Java avaient fort à faire à l'époque.

31 votes

"En tant que bonne pratique de programmation (dans n'importe quel langage), vous ne devriez jamais réassigner une variable paramètre/argument [ ]" Désolé, je dois vraiment vous interpeller sur ce point. La réaffectation d'arguments est une pratique courante dans des langages comme le Javascript, où le nombre d'arguments transmis (ou même s'il y en a) n'est pas dicté par la signature de la méthode. Par exemple, avec une signature comme : "function say(msg)", les gens s'assureront que l'argument "msg" est assigné, comme suit : "msg = msg || 'Hello World!' ;". Les meilleurs programmeurs Javascript du monde ne respectent pas vos bonnes pratiques. Il suffit de lire les sources de jQuery.

59 votes

@StijndeWitt Votre exemple montre justement le problème de la réaffectation de la variable argument. Vous perdez de l'information avec rien de gagné en retour : (a) vous avez perdu la valeur originale transmise, (b) vous avez perdu l'intention de la méthode d'appel (l'appelant a-t-il transmis 'Hello World!' ou avons-nous fait défaut). Les deux méthodes a et b sont utiles pour les tests, les codes longs et lorsque la valeur est modifiée ultérieurement. Je maintiens ma déclaration : arg vars devrait jamais être réaffectés. Votre code devrait être le suivant : message = ( msg || 'Hello World"' ) . Il n'y a tout simplement aucune raison no Le seul coût est de quelques octets de mémoire.

3 votes

@StijndeWitt Un article intéressant sur ce sujet : lpar.ath0.com/2008/08/26/java-annoyance-paramètres-finaux

239voto

jsha Points 857

Parfois, il est bon d'indiquer explicitement (pour plus de lisibilité) que la variable ne change pas. Voici un exemple simple où l'utilisation de final peut vous éviter quelques maux de tête.

public void setTest(String test) {
    test = test;
}

si vous oubliez le mot-clé 'this' dans un paramètre, la variable que vous voulez définir ne sera pas définie. Cependant, si vous utilisez le mot-clé final sur le paramètre, le bogue sera détecté au moment de la compilation.

70 votes

Btw vous verrez l'avertissement "L'affectation à la variable test n'a pas d'effet" de toute façon

13 votes

@AvrDragon Mais nous pourrions aussi ignorer l'avertissement. Il est donc toujours préférable d'avoir quelque chose qui nous empêche d'aller plus loin, comme une erreur de compilation, que nous obtiendrons en utilisant le mot-clé final.

11 votes

@AvrDragon Cela dépend de l'environnement de développement. Vous ne devriez pas compter sur l'IDE pour attraper ce genre de choses pour vous de toute façon, à moins que vous ne vouliez développer de mauvaises habitudes.

131voto

Jon Skeet Points 692016

Oui, si l'on exclut les classes anonymes, la lisibilité et la déclaration d'intention, c'est presque sans valeur. Ces trois choses sont-elles sans valeur pour autant ?

Personnellement, j'ai tendance à ne pas utiliser final pour les variables locales et les paramètres à moins que j'utilise la variable dans une classe interne anonyme, mais je peux certainement comprendre le point de vue de ceux qui veulent qu'il soit clair que la valeur du paramètre lui-même ne changera pas (même si l'objet auquel il se réfère change son contenu). Pour ceux qui trouvent que cela ajoute à la lisibilité, je pense que c'est une chose tout à fait raisonnable à faire.

Votre remarque serait plus importante si quelqu'un prétendait réellement que c'est a fait maintenir les données constantes alors qu'elles ne le sont pas - mais je ne me souviens pas avoir vu de telles affirmations. Suggérez-vous qu'il y a un corps significatif de développeurs suggérant que final a plus d'effet qu'il n'en a réellement ?

EDIT : J'aurais vraiment dû résumer tout cela par une référence aux Monty Python ; la question ressemble un peu à la question "Qu'est-ce que les Romains ont jamais fait pour nous ?".

19 votes

Mais pour paraphraser Krusty et son danois, qu'ont-ils fait pour nous ? LATELY ? =)

0 votes

Yuval. C'est très drôle ! Je suppose que la paix peut exister même si elle est imposée par la lame d'une épée !

2 votes

La question semble plus proche de celle de savoir "Qu'est-ce que l'on peut faire ? n'ont pas les Romains ont-ils fait pour nous ?", parce qu'il s'agit plutôt d'une critique de ce que le mot-clé final a fait pour nous. ne faire.

80voto

Michael Borgwardt Points 181658

Laissez-moi vous expliquer un peu le cas où vous ont d'utiliser final, que Jon a déjà mentionné :

Si vous créez une classe interne anonyme dans votre méthode et que vous utilisez une variable locale (comme un paramètre de méthode) dans cette classe, le compilateur vous oblige à rendre le paramètre final :

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

Ici, le from et to Les paramètres doivent être finaux 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 la classe anonyme est renvoyée par la méthode, elle peut donc vivre beaucoup plus longtemps. Vous ne pouvez pas préserver la pile, car elle est nécessaire pour les appels de méthode ultérieurs.

Donc ce que Java fait à la place est de mettre 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 byte code). Mais si elles n'étaient pas finales, on pourrait s'attendre à ce que la classe anonyme et la méthode voient les changements que l'autre fait à la variable. Afin de maintenir l'illusion qu'il n'y a qu'une seule variable plutôt que deux copies, elle doit être finale.

1 votes

Vous m'avez perdue à partir de "Mais s'ils ne sont pas final....". Pouvez-vous essayer de le reformuler, je n'ai peut-être pas bu assez de café.

1 votes

Vous avez une variable locale from - la question est de savoir ce qui se passe si vous utilisez l'instance de classe anon à l'intérieur de la méthode et qu'elle modifie les valeurs de from - les gens s'attendraient à ce que le changement soit visible dans la méthode, puisqu'ils ne voient qu'une seule variable. Pour éviter cette confusion, it doit être final.

0 votes

Il ne s'agit pas d'une copie, mais simplement d'une référence à l'objet référencé.

28voto

Fortyrunner Points 8072

J'utilise le final tout le temps sur les paramètres.

Cela ajoute-t-il tant que ça ? Pas vraiment.

Est-ce que je l'éteindrais ? Non.

La raison : j'ai trouvé 3 bogues dans lesquels des personnes avaient écrit du code peu soigné et avaient omis de définir une variable membre dans les accesseurs. Tous les bugs se sont avérés difficiles à trouver.

J'aimerais que cela devienne la valeur par défaut dans une future version de Java. Le passage par valeur/référence fait trébucher un grand nombre de jeunes programmeurs.

Une dernière chose mes méthodes ont tendance à avoir un faible nombre de paramètres, donc le texte supplémentaire sur une déclaration de méthode n'est pas un problème.

4 votes

J'étais sur le point de suggérer cela aussi, que final soit la valeur par défaut dans les versions futures et qu'il faille spécifier "mutable" ou un meilleur mot-clé qui soit conçu. Voici un article intéressant à ce sujet : lpar.ath0.com/2008/08/26/java-annoyance-paramètres-finaux

0 votes

Cela fait longtemps, mais pourriez-vous donner un exemple du bogue que vous avez détecté ?

0 votes

Voir la réponse la plus votée. Il s'agit d'un très bon exemple où la variable membre n'est pas définie et où le paramètre est muté.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X