web-dev-qa-db-fra.com

Quand faut-il utiliser final pour les paramètres de méthode et les variables locales?

J'ai trouvé quelques références ( par exemple ) suggérant d'utiliser final autant que possible et je me demande à quel point c'est important. C’est principalement dans le contexte des paramètres de méthode et des variables locales, et non des méthodes finales ou des classes. Pour les constantes, c'est logique.

D'une part, le compilateur peut effectuer certaines optimisations et clarifie l'intention du programmeur. D'autre part, cela ajoute de la verbosité et les optimisations peuvent être triviales.

Est-ce quelque chose que je devrais faire un effort de ne pas oublier?

156
eaolson

Obsess sur:

  • Champs finaux - Marquer les champs comme finaux les oblige à être définis avant la fin de la construction, rendant cette référence immuable. Cela permet une publication sécurisée des champs et évite le besoin de synchronisation sur les lectures ultérieures. (Notez que pour une référence d'objet, seule la référence de champ est immuable - les éléments auxquels la référence d'objet fait référence peuvent toujours changer et cela affecte l'immutabilité.)
  • Champs statiques finaux - Bien que j'utilise maintenant des énumérations pour la plupart des cas où j’utilisais des champs finaux statiques.

Considérez mais utilisez judicieusement:

  • Classes finales - La conception Framework/API est le seul cas où je l’envisage.
  • Méthodes finales - Fondamentalement les mêmes que les classes finales. Si vous utilisez des modèles de méthode de modèle comme crazy et que vous marquez des éléments finaux, vous vous appuyez probablement trop sur l'héritage et pas assez sur la délégation. 

Ignorer sauf si je me sens anale:

  • Paramètres de méthode et variables locales - JE FAIS RARELLEMENT cela principalement parce que je suis paresseux et que je trouve que cela encombre le code. Je vais bien admettre que les paramètres de marquage et les variables locales que je ne vais pas modifier sont "plus justes". Je souhaite que ce soit la valeur par défaut. Mais ce n’est pas le cas et je trouve le code plus difficile à comprendre avec les finales. Si je suis dans le code de quelqu'un d'autre, je ne le sortirai pas, mais si j'écris un nouveau code, je ne le mettrai pas. Une exception est le cas où vous devez marquer quelque chose de final pour pouvoir de l'intérieur d'une classe interne anonyme. 
161
Alex Miller

Est-ce quelque chose que je devrais faire un effort de ne pas oublier?

Non, si vous utilisez Eclipse, car vous pouvez configurer une action de sauvegarde pour ajouter automatiquement ces modificateurs final à votre place. Ensuite, vous obtenez les avantages pour moins d'effort.

43
Peter Hilton

Les avantages de "final" au moment du développement sont au moins aussi importants que les avantages à l’exécution. Il informe les futurs éditeurs du code de vos intentions.

Marquer une classe "final" indique que vous n'avez pas fait d'effort lors de la conception ou de la mise en œuvre de la classe pour gérer l'extension correctement. Si les lecteurs peuvent apporter des modifications à la classe et veulent supprimer le modificateur "final", ils peuvent le faire à leurs propres risques. C'est à eux de s'assurer que la classe gérera bien l'extension.

Marquer une variable "finale" (et l'affecter dans le constructeur) est utile avec l'injection de dépendance. Il indique le caractère "collaborateur" de la variable.

Marquer une méthode "finale" est utile dans les classes abstraites. Il indique clairement où se trouvent les points d’extension. 

15
Eric R. Rath

J'ai trouvé que les paramètres de la méthode de marquage et les paramètres locaux sous la forme final sont utiles comme aide au refactoring lorsque la méthode en question est un fouillis incompréhensible de plusieurs pages. Saupoudrez final libéralement, voyez ce que le compilateur (ou votre IDE) génère comme erreurs "ne peut pas affecter à la variable finale", et vous découvrirez peut-être pourquoi la variable appelée "data" est nulle même si plusieurs commentaires (périmés) jure ça ne peut pas arriver.

Ensuite, vous pouvez corriger certaines des erreurs en remplaçant les variables réutilisées par de nouvelles variables déclarées plus proches du point d'utilisation. Ensuite, vous constaterez que vous pouvez envelopper des parties entières de la méthode dans des accolades et que vous êtes tout à coup une touche IDE loin de "Extract Method" et que votre monstre est devenu plus compréhensible.

Si votre méthode est pas déjà une épave impossible à maintenir, je suppose qu’il serait peut-être utile de rendre une chose définitive pour décourager les gens de la transformer en ladite épave; mais si c'est une méthode courte (voir: non intenable), vous risquez d'ajouter beaucoup de verbosité. En particulier, les signatures de fonctions Java sont assez difficiles à insérer dans 80 caractères, sans ajouter six autres par argument!

9
Sam Stokes

J'utilise final tout le temps pour rendre Java plus basé sur l'expression. Voir les conditions de Java (if,else,switch) ne sont pas basées sur des expressions, ce que j'ai toujours détesté, surtout si vous étiez habitué à la programmation fonctionnelle (ML, Scala ou LISP).

Ainsi, vous devriez essayer de toujours (IMHO) utiliser des variables finales lorsque vous utilisez des conditions.

Laisse moi te donner un exemple:

    final String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
        default:
            throw new IllegalStateException();
    }

Maintenant, si vous ajoutez une autre instruction case et ne définissez pas name, le compilateur échouera. Le compilateur échouera également si vous ne cassez pas tous les cas (que vous définissiez la variable). Cela vous permet de rendre Java très similaire aux expressions let de LISP et de faire en sorte que votre code ne soit pas indenté massivement (en raison de variables de portée lexicales).

Et comme @Recurse a noté (mais apparemment -1 moi), vous pouvez faire ce qui précède en faisant String namefinal pour obtenir l’erreur du compilateur (ce que j’avais jamais dit que vous ne pouviez pas), mais vous pouvez facilement faire disparaître l’erreur du compilateur après avoir l'instruction switch qui jette la sémantique de l'expression ou pire, en oubliant break à laquelle vous ne pouvez pas causer d'erreur (malgré ce que dit @Recurse) sans utiliser final:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            //break; whoops forgot break.. 
            //this will cause a compile error for final ;P @Recurse
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

En raison du nom du paramètre de bogue (en plus d'oublier break qui contient aussi un autre bogue), je peux maintenant le faire accidentellement:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        //should have handled all the cases for pluginType
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

La variable finale force une évaluation unique de ce que le nom devrait être. Semblable à la manière dont une fonction qui a une valeur de retour doit toujours renvoyer une valeur (en ignorant les exceptions), le bloc de commutateurs de noms devra résoudre name et sera donc lié à ce bloc de commutateurs, ce qui facilite la refactorisation de morceaux de code (méthode Eclipe refactor: extract). .

Le ci-dessus en OCaml:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> "Candidate Stuff"
    | JobPostingImport -> "Blah" ;;

Le match ... with ... est évalué comme une fonction, c'est-à-dire une expression. Remarquez comment cela ressemble à notre instruction switch.

Voici un exemple dans Scheme (Racket or Chicken):

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))
8
Adam Gent

C'est utile dans les paramètres pour éviter de changer la valeur du paramètre par accident et introduire un bogue subtil. J'ignorais cette recommandation, mais après avoir passé 4 heures. Dans une méthode horrible (avec des centaines de lignes de code et de multiples fors, des ifs imbriquées et toutes sortes de mauvaises pratiques), je vous recommanderais de le faire.

 public int processSomethingCritical( final int x, final int y ){
 // hundreds of lines here 
     // for loop here...
         int x2 = 0;
        x++; // bug aarrgg...
 // hundreds of lines there
 // if( x == 0 ) { ...

 }

Bien sûr, dans un monde parfait, cela ne se produirait pas, mais… bon… parfois, vous devez supporter le code des autres. :( 

6
OscarRyz

Eh bien, tout dépend de votre style ... si vous aimez voir la finale lorsque vous ne modifiez pas la variable, utilisez-la. Si vous N'AIMEZ PAS le voir ... alors laissez-le de côté.

Personnellement, j'aime aussi peu de verbosité que possible, aussi j'ai tendance à éviter d'utiliser des mots clés supplémentaires qui ne sont pas vraiment nécessaires.

Je préfère cependant les langages dynamiques, alors ce n’est donc pas une surprise si j’aime éviter la verbosité.

Donc, je dirais simplement de choisir la direction vers laquelle vous vous penchez et de vous y mettre (dans tous les cas, essayez d'être cohérent).


En passant, j'ai travaillé sur des projets qui utilisent et n'utilisent pas un tel motif, et je n'ai vu aucune différence dans la quantité de bugs ou d'erreurs ... Je ne pense pas que ce soit un motif qui va énormément améliorez le nombre de bogues ou quoi que ce soit, mais encore une fois, c’est du style, et si vous aimez exprimer l’intention de ne pas le modifier, continuez et utilisez-le.

6
Mike Stone

Si vous écrivez une application pour laquelle une personne devra lire le code après, disons, un an, alors oui, utilisez la variable final sur qui ne doit pas être modifiée en permanence. En faisant cela, votre code sera plus "auto-documenté" et vous réduirez également les chances des autres développeurs de faire des choses stupides, comme utiliser une constante locale en tant que variable temporaire locale. 

Si vous écrivez un code jetable, alors, non, ne vous souciez pas d'identifier toutes les constantes et de les rendre définitives.

5
Alvin

Je vais utiliser final autant que je peux. Cela vous signalera si vous modifiez le champ par inadvertance. J'ai également défini les paramètres de la méthode à final. Ce faisant, j’ai attrapé plusieurs bogues du code que j’ai pris en charge lorsqu’ils essayaient de "définir" un paramètre en oubliant les passes Java par valeur.

4
Javamann

La variable final a de nombreuses utilisations. En voici quelques unes 

Constantes finales

 public static class CircleToolsBetter {
     public final static double PI = 3.141;
        public double getCircleArea(final double radius) {
          return (Math.pow(radius, 2) * PI);
        }
    }

Cela peut être utilisé ensuite pour d'autres parties de vos codes, ou accessible par d'autres classes, de cette façon, si jamais vous changiez la valeur, vous ne seriez pas obligé de les changer un par un.

Variables finales

public static String someMethod(final String environmentKey) {
    final String key = "env." + environmentKey;
    System.out.println("Key is: " + key);
    return (System.getProperty(key));

  }

}

Dans cette classe, vous construisez une variable finale étendue qui ajoute un préfixe au paramètre environmentKey. Dans ce cas, la variable finale est finale uniquement dans la portée d'exécution, qui est différente à chaque exécution de la méthode. Chaque fois que la méthode est entrée, la finale est reconstruite. Dès qu'il est construit, il ne peut plus être modifié pendant l'exécution de la méthode. Cela vous permet de fixer une variable dans une méthode pour la durée de la méthode. voir ci-dessous:

public class FinalVariables {


  public final static void main(final String[] args) {
    System.out.println("Note how the key variable is changed.");
    someMethod("Java_HOME");
    someMethod("ANT_HOME");
  }
}

Constantes finales

public double equation2Better(final double inputValue) {
    final double K = 1.414;
    final double X = 45.0;

double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;         
if (result > 360) {
  powInputValue = X * Math.sin(result); 
} else {
  inputValue = K * Math.sin(result);   // <= Compiler error   
}

Celles-ci sont particulièrement utiles lorsque vous avez de très longues lignes de codes et que cela générera une erreur du compilateur afin que vous ne rencontriez pas d'erreur logique/commerciale lorsque quelqu'un modifie accidentellement des variables qui ne devraient pas l'être.

Collections finales

Différents cas lorsque nous parlons de Collections, vous devez les définir comme non modifiables.

 public final static Set VALID_COLORS; 
    static {
      Set temp = new HashSet( );
      temp.add(Color.red);
      temp.add(Color.orange);
      temp.add(Color.yellow);
      temp.add(Color.green);
      temp.add(Color.blue);
      temp.add(Color.decode("#4B0082")); // Indigo
      temp.add(Color.decode("#8A2BE2")); // Violet
      VALID_COLORS = Collections.unmodifiableSet(temp);
    }

sinon, si vous ne le définissez pas comme non modifiable:

Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler

Classes finales et Méthodes finales ne peuvent être étendues ou écrasées respectivement.

EDIT: POUR ABORDER LE PROBLÈME DE CLASSE FINAL CONCERNANT L’ENCAPSULATION:

Il y a deux façons de faire une classe finale. La première consiste à utiliser le mot-clé final dans la déclaration de classe:

public final class SomeClass {
  //  . . . Class contents
}

La deuxième façon de rendre une classe finale est de déclarer tous ses constructeurs comme privés:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Le marquer comme final vous évite d'avoir à vérifier qu'il s'agit bien d'une finale, ce qui prouve que vous regardez bien cette classe de tests. regarde le public à première vue.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Malheureusement, le seul constructeur de la classe étant privé, il est impossible d'étendre cette classe. Dans le cas de la classe Test, il n'y a aucune raison pour que la classe soit finale. La classe Test est un bon exemple de la façon dont les classes finales implicites peuvent causer des problèmes.

Vous devez donc le marquer comme final lorsque vous rendez implicitement une classe finale en rendant son constructeur privé.

2
mel3kings

La question ne dit pas clairement si cela est évident, mais rendre un paramètre de méthode final n’affecte que le corps de la méthode. Cela ne donne PAS des informations intéressantes sur les intentions de la méthode au demandeur. L'objet transmis peut toujours être muté au sein de la méthode (les finales ne sont pas des const) et la portée de la variable est à l'intérieur de la méthode.

Pour répondre à votre question précise, je ne prendrais pas la peine de rendre une instance ou une variable locale (y compris les paramètres de méthode) finale à moins que le code ne l'exige (par exemple, la variable est référencée depuis une classe interne), ou pour clarifier une logique très compliquée.

Par exemple, les variables, je les ferais final si elles sont logiquement constantes.

2
ykaganovich

Un peu comme un compromis que vous avez mentionné, mais je préfère l’utilisation explicite de quelque chose que l’utilisation implicite. Cela aidera à lever certaines ambiguïtés pour les futurs responsables du code, même si ce n’est que vous. 

1
Sean

Si vous avez des classes internes (anonymes) et que la méthode doit accéder à la variable de la méthode conteneur, vous devez définir cette variable en tant que final.

Autre que cela, ce que vous avez dit est juste.

1
anjanb

Une réponse très simple nous avons 3 cas avec Final avec variables, Final avec méthodes && Final avec classes.

1.Final avec variable: vous ne pouvez pas affecter cette variable plus d’une fois.

2.Final avec des méthodes: u ne peut pas écraser cette méthode ..

3.Final avec des classes: vous ne pouvez pas prolonger une classe finale

0
Mohamed Ayed

Utilisez le mot clé final pour une variable si vous définissez cette variable sous la forme immutable.

En déclarant la variable comme étant finale, elle aide les développeurs à exclure d'éventuels problèmes de modification de variables dans un environnement hautement multithread.

Avec la version Java 8, nous avons un autre concept appelé "effectively final variable". Une variable non finale peut être levée comme variable finale.

les variables locales référencées à partir d'une expression lambda doivent être finales ou effectivement définitives

Une variable est considérée comme finale effective si elle n'est pas modifiée après l'initialisation dans le bloc local. Cela signifie que vous pouvez maintenant utiliser la variable locale sans mot-clé final dans une classe anonyme ou une expression lambda, à condition qu'elles soient effectivement définitives.

Jusqu'à Java 7, vous ne pouvez pas utiliser une variable locale non finale dans une classe anonyme, mais à partir de Java 8, vous pouvez

Regardez cet article article

0
Ravindra babu