Faire des arguments de méthode Java comme finaux

91

Quelle différence cela finalfait entre le code ci-dessous. Y a-t-il un avantage à déclarer les arguments comme final.

public String changeTimezone( Timestamp stamp, Timezone fTz, Timezone toTz){  
    return ....
}

public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
        final Timezone toTz){
    return ....
}
John
la source
4
Il existe des analyseurs de code qui avertissent si un paramètre est réutilisé ou réaffecté. (Idem pour les variables locales) À mon humble avis, c'est une meilleure façon d'attraper de tels paramètres si vous trouvez que les changer est indésirable.
Peter Lawrey
Je pense que Java devrait rendre tous les arguments de la méthode d'entrée comme finaux par défaut. Et puis si je veux modifier la référence, il faudrait que je le fasse manuellement. De cette façon, le facteur de culpabilité empêcherait de nombreux cas de ce genre.
Sid

Réponses:

131

Comme un paramètre de méthode formel est une variable locale, vous pouvez y accéder à partir de classes anonymes internes uniquement si elles sont déclarées comme finales.

Cela vous évite de déclarer une autre variable finale locale dans le corps de la méthode:

 void m(final int param) {
        new Thread(new Runnable() {
            public void run() {
                System.err.println(param);
            }
        }).start();
    }
PeterMmm
la source
27
+1: C'est un cas d'utilisation important, et le seul moment où vous en avez besoin . (Le reste du temps, ce n'est qu'une question de ce qui est pratique pour aider le programmeur.)
Donal Fellows
3
Puis-je demander le raisonnement derrière cela?
KodeWarrior
20
Plus nécessaire avec Java 8.
Amit Parashar
3
@simgineer lire ici: stackoverflow.com/questions/28408109/…
PeterMmm
3
@AmitParashar Vrai, mais Java 8 vous évite simplement d'avoir à utiliser le mot-clé "final" chaque fois que vous devez utiliser la variable dans une classe interne ... La réalité est que le compilateur ne fait que rendre la finalité implicite, vous besoin que la variable soit effectivement définitive ... Donc, vous obtenez toujours une erreur de compilation au cas où vous essayez de l'assigner plus tard! Java 8 : SNEAK 100 :)
varun
38

Extrait du dernier mot sur le mot-clé final

Paramètres finaux

L'exemple suivant déclare les paramètres finaux:

public void doSomething(final int i, final int j)
{
  // cannot change the value of i or j here...
  // any change would be visible only inside the method...
}

final est utilisé ici pour garantir que les deux index i et j ne seront pas réinitialisés accidentellement par la méthode. C'est un moyen pratique de se protéger contre un bug insidieux qui modifie par erreur la valeur de vos paramètres. De manière générale, les méthodes courtes sont un meilleur moyen de se protéger de cette classe d'erreurs, mais les paramètres finaux peuvent être un ajout utile à votre style de codage.

Notez que les paramètres finaux ne sont pas considérés comme faisant partie de la signature de la méthode et sont ignorés par le compilateur lors de la résolution des appels de méthode. Les paramètres peuvent être déclarés finaux (ou non) sans influence sur la façon dont la méthode est remplacée.

pgras
la source
17
Il serait peut-être préférable d'utiliser des objets plutôt que des primitives pour cet exemple, car les modifications primitives ne seront toujours visibles qu'à l'intérieur de la méthode. Et dans le cas des objets, vous pouvez toujours les modifier. Vous ne pouvez tout simplement pas pointer vers un nouvel objet. En fait maintenant j'y pense, final ne change vraiment rien par rapport au fait de le laisser de côté, à part enregistrer une déclaration de variable avec les AIC et demander au compilateur de signaler les modifications accidentelles de paramètres que vous ne vouliez pas modifier pour une raison quelconque .
Rob Grant
26

Le final vous empêche d'attribuer une nouvelle valeur à la variable, ce qui peut être utile pour détecter les fautes de frappe. Stylistiquement, vous voudrez peut-être garder les paramètres reçus inchangés et les affecter uniquement aux variables locales, donc final aiderait à appliquer ce style.

Je dois admettre que je me souviens rarement d'utiliser final pour les paramètres, peut-être que je devrais le faire.

public int example(final int basicRate){
    int discountRate;

    discountRate = basicRate - 10;
    // ... lots of code here 
    if ( isGoldCustomer ) {
        basicRate--;  // typo, we intended to say discountRate--, final catches this
    }
    // ... more code here

    return discountRate;
}
djna
la source
1
Excellent exemple de la façon dont la déclaration d'arguments comme final peut être utile. Je suis partisan de cela, mais c'est aussi une bouchée pour plus de 3 paramètres.
JoseHdez_2
14

Cela ne fait pas beaucoup de différence. Cela signifie simplement que vous ne pouvez pas écrire:

stamp = null;
fTz = new ...;

mais vous pouvez toujours écrire:

stamp.setXXX(...);
fTz.setXXX(...);

C'est principalement un indice pour le programmeur de maintenance qui vous suit que vous n'allez pas attribuer une nouvelle valeur au paramètre quelque part au milieu de votre méthode où cela n'est pas évident et pourrait donc prêter à confusion.

Adrian Pronk
la source
3

Le mot clé final lorsqu'il est utilisé pour les paramètres / variables en Java marque la référence comme finale. En cas de passage d'un objet à une autre méthode, le système crée une copie de la variable de référence et la transmet à la méthode. En marquant les nouvelles références comme étant définitives, vous les protégez d'une réaffectation. C'est parfois considéré comme une bonne pratique de codage.

Sid
la source
1
Je dois ajouter quelque chose: si les paramètres sont primitifs, je ne vois aucune différence. De plus, si les paramètres sont des Collections (une liste d'objets ...), l'ajout final ne pourrait pas empêcher leur modification.
Sam003 du
1
L'immuabilité est toujours un trait souhaitable. Java ne l'a pas prêt à l'emploi. Rendre les variables définitives garantit au moins l'intégrité de la référence.
Sid
1
Je suis d'accord. Mais si nous voulons vraiment obtenir l'immuabilité des objets, nous pourrions essayer de faire un clone profond.
Sam003 du
2

Pour le corps de cette méthode, le finalmot - clé empêchera les références d'argument d'être réassignées accidentellement, donnant une erreur de compilation dans ces cas (la plupart des IDE se plaindront immédiatement). Certains peuvent affirmer que l'utilisation finalen général chaque fois que possible accélérera les choses, mais ce n'est pas le cas dans les JVM récentes.

dimitrisli
la source
2

Deux avantages que je vois sont énumérés:

1 Marquer l'argument de la méthode comme final empêche la réaffectation de l'argument à l'intérieur de la méthode

De votre exemple

    public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
            final Timezone toTz){
    
    // THIS WILL CAUSE COMPILATION ERROR as fTz is marked as final argument

      fTz = Calendar.getInstance().getTimeZone();     
      return ..
    
    }

Dans une méthode compliquée, marquer les arguments comme finaux aidera à interpréter accidentellement ces arguments en tant que méthodes, les variables locales et les réaffecter en tant que compilateur marquera ces cas comme indiqué dans l'exemple.

2 Passer l'argument à une classe interne anonyme

Comme un paramètre de méthode formel est une variable locale, vous pouvez y accéder à partir de classes anonymes internes uniquement si elles sont déclarées comme finales.

Santhosh Urumese
la source
1

- Dans le passé (avant Java 8 :-))

L'utilisation explicite du mot clé "final" a affecté l'accessibilité de la variable de méthode pour les classes anonymes internes.

- Dans le langage moderne (Java 8+), une telle utilisation n'est pas nécessaire:

Java a introduit des variables "effectivement finales". Les variables locales et les paramètres de méthode sont supposés définitifs si le code n'implique pas de changement de valeur de la variable. Donc, si vous voyez un tel mot-clé dans Java8 +, vous pouvez supposer que ce n'est pas nécessaire. L'introduction de "effectivement final" nous fait taper moins de code lors de l'utilisation de lambdas.

Witold Kaczurba
la source
0

C'est juste une construction en Java pour vous aider à définir un contrat et à vous y tenir. Une discussion similaire ici: http://c2.com/cgi/wiki?JavaFinalConsemedEvil

BTW - (comme le dit le twiki), marquer les arguments comme finaux est généralement redondant si vous suivez de bons principes de programmation et que vous avez réaffecté / redéfini la référence d'argument entrant.

Dans le pire des cas, si vous redéfinissez la référence args, cela n'affectera pas la valeur réelle passée à la fonction - car seule une référence a été passée.

madhurtanwani
la source
0

Je parle de marquer les variables et les champs comme final en général - ne s'applique pas uniquement aux arguments de méthode. (Marquer les méthodes / classes comme finales est une tout autre chose).

C'est une faveur pour les lecteurs / futurs mainteneurs de votre code. Avec un nom sensé de la variable, il est utile et rassurant pour le lecteur de votre code de voir / comprendre ce que représentent les variables en question - et il est rassurant pour le lecteur que chaque fois que vous voyez la variable dans la même portée, la signification reste pareil, donc il n'a pas à se gratter la tête pour toujours comprendre ce que signifie une variable dans chaque contexte. Nous avons vu trop d'abus de «réutilisation» de variables, ce qui rend même un court extrait de code difficile à comprendre.

RAYON
la source
-3

Le mot-clé final vous empêche d'attribuer une nouvelle valeur au paramètre. Je voudrais expliquer cela avec un exemple simple

Supposons que nous ayons une méthode

method1 () {

Date dateOfBirth = nouvelle date ("1/1/2009");

method2 (dateOfBirth);

method3 (dateOfBirth); }

public mehod2 (Date dateOfBirth) {
....
....
....
}

public mehod2 (Date dateOfBirth) {
....
....
....
}

Dans le cas ci-dessus, si "dateOfBirth" reçoit une nouvelle valeur dans method2, cela entraînerait une sortie erronée de method3. Comme la valeur transmise à method3 n'est pas ce qu'elle était avant d'être transmise à method2. Donc pour éviter ce dernier mot-clé est utilisé pour les paramètres.

Et c'est également l'une des meilleures pratiques de codage Java.

Kamal
la source
5
Ce n’est pas tout à fait vrai. Même si l'argument dateOfBirth est changé en une autre valeur dans method2 (), cela n'aurait aucun effet sur method2 (), puisque Java passe par valeur et non par référence.
Flo