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

StackOverflow https://stackoverflow.com/questions/154314

  •  03-07-2019
  •  | 
  •  

Question

J'ai trouvé quelques références ( par exemple . ) qui suggèrent 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, cela a du sens.

D'une part, le compilateur peut procéder à 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 pour me souvenir?

Était-ce utile?

La solution

Obsed over:

  • Champs finaux - Marquer les champs comme étant définitifs les oblige à les définir avant la fin de la construction, ce qui rend 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 comptez probablement trop sur l'héritage et pas assez sur la délégation.

Ignorer si vous ne vous sentez pas anal:

  • 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 partout. 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 y accéder. de l'intérieur d'une classe interne anonyme.

Autres conseils

  

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

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.

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

Marquer une classe " final " indique que vous n'avez pas essayé, lors de la conception ou de la mise en œuvre de la classe, de gérer l'extension avec élégance. Si les lecteurs peuvent apporter des modifications à la classe et souhaitent supprimer le paramètre "final" modificateur, ils peuvent le faire à leurs propres risques. C’est à eux de s’assurer que la classe gérera bien l’extension.

Marquage d'une variable " final " (et l'affecter dans le constructeur) est utile avec l'injection de dépendance. Il indique le " collaborateur " nature de la variable.

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

J'ai trouvé que les paramètres de la méthode de marquage et les paramètres locaux comme final sont utiles en tant qu'aide au refactoring lorsque la méthode en question est un gâchis incompréhensible de plusieurs pages. Saupoudrer final librement, voir ce que " ne peut pas affecter à la variable finale " erreurs que le compilateur (ou votre IDE) génère, et vous pourriez découvrir pourquoi la variable appelée "data" finit par null même si plusieurs commentaires (obsolètes) ne jurent que par impossibilité.

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. Vous constaterez ensuite que vous pouvez envelopper des parties entières de la méthode entre accolades, puis vous êtes tout à coup à l’abri de la touche "Extraire la méthode". et votre monstre est maintenant plus compréhensible.

Si votre méthode n'est pas une épave incontrôlable, je suppose qu'il serait peut-être utile de rendre une chose finale pour décourager les gens de la transformer en ladite épave; mais si c'est une méthode courte (voir: non intempestive), vous risquez d'ajouter beaucoup de verbosité. En particulier, les signatures de fonction Java sont assez difficiles à insérer dans 80 caractères tels quels sans en ajouter six de plus par argument!

J'utilise final tout le temps pour que Java soit davantage basé sur l'expression. Voir les conditions de Java ( si, sinon, basculer ) ne sont pas basées sur des expressions, ce que j'ai toujours détesté, en particulier si vous étiez habitué à la programmation fonctionnelle (ML, Scala ou Lisp).

Ainsi, vous devriez essayer de toujours utiliser (à mon humble avis) les variables finales lorsque vous utilisez des conditions.

Laissez-moi vous 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 nom , 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 sans faire de nom de chaîne final pour obtenir l'erreur du compilateur (ce que je n'ai jamais dit vous ne pourriez pas) mais vous pourriez facilement faire disparaître l’erreur du compilateur en définissant le nom après l’instruction switch qui jette la sémantique de l’expression ou pire en oubliant break , ce que vous ne pouvez pas provoquer d’erreur dit) 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 de l’oubli de break qui est également 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). .

Ce qui précède dans OCaml:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

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

La correspondance ... avec ... est évaluée comme une fonction, c'est-à-dire une expression. Remarquez l’apparence de notre instruction switch.

Voici un exemple dans Scheme (raquette ou poulet):

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

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, j'ai donc tendance à éviter d'utiliser des mots clés supplémentaires qui ne sont pas vraiment nécessaires.

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

Alors, je dirais simplement de choisir la direction dans laquelle vous vous penchez et de vous y mettre (quel que soit le 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 pas constaté de différence dans le nombre de bugs ou d’erreurs ... Je ne pense pas que ce soit un motif cela améliorera énormément votre 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.

Ceci 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 d’autres. :(

Si vous écrivez une application qui demandera à quelqu'un de lire le code après, disons, un an, alors oui, utilisez la variable finale sur qui ne doit pas être modifiée à tout moment. En faisant cela, votre code sera plus "auto-documenté". De plus, vous réduisez les chances pour d'autres développeurs de faire des bêtises, telles que l'utilisation d'une constante locale en tant que variable temporaire locale.

Si vous écrivez un code jetable, alors, non, ne vous souciez pas d'identifier toute la constante et de la rendre finale.

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 liés au code que j’ai pris en charge lorsqu’ils tentaient de "définir" un paramètre en oubliant les passes Java par valeur.

La question ne nous dit pas clairement si cela est évident, mais définir un paramètre de méthode final n’affecte que le corps de la méthode. NOT ne transmet aucune information intéressante sur les intentions de la méthode au demandeur. L'objet transmis peut toujours être muté dans la méthode (les finales ne sont pas des const), et la portée de la variable est dans 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 à partir d'une classe interne), ou pour clarifier une logique très compliquée. .

Pour les variables d'instance, je les rendrais définitives si elles sont logiquement constantes.

La variable final a de nombreuses utilisations. Voici juste quelques

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);
        }
    }

Ceci peut être utilisé ensuite pour d'autres parties de vos codes, ou utilisé par d'autres classes. Ainsi, si 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 lorsqu'une personne 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
Les

classes finales et les méthodes finales ne peuvent être ni étendues ni écrasées, respectivement.

EDIT: POUR ABORDER LE PROBLÈME DE LA CATÉGORIE FINAL CONCERNANT L’ENCAPSULATION:

Il y a deux façons de rendre 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 fait de le marquer comme final vous évite des problèmes si vous découvrez qu'il s'agit bien d'une finale, ce qui vous permettra de regarder ce qui est fait dans 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 définissez implicitement une classe en rendant son constructeur privé.

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.

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 dernière.

À part cela, ce que vous avez dit est juste.

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

.

En déclarant la variable comme étant finale, les développeurs sont ainsi mieux à même d’exclure d'éventuels problèmes de modification de variables dans un environnement hautement multithread.

Avec la version 8 de java, nous avons un autre concept appelé " effectivement variable finale ". Une variable non finale peut être levée en tant que 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 effective si elle n'est pas modifiée après l'initialisation dans le bloc local. Cela signifie que vous pouvez désormais 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

Consultez cet article

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

1.Final avec variable: vous ne pouvez pas affecter cette variable plusieurs fois.

2.Final avec les méthodes: vous ne pouvez pas écraser cette méthode.

3.Final avec les classes: vous ne pouvez étendre aucune classe finale

Tout d’abord, le mot-clé final est utilisé pour rendre une variable constante. Constante signifie que cela ne change pas. Par exemple:

final int CM_PER_INCH = 2.54;

Vous déclareriez la variable finale car un centimètre par pouce ne change pas.

Si vous essayez de remplacer une valeur finale, la variable correspond à ce qu'elle a été déclarée en premier. Par exemple:

final String helloworld = "Hello World";
helloworld = "A String"; //helloworld still equals "Hello World"

Il y a une erreur de compilation qui ressemble à:

local variable is accessed from inner class, must be declared final

Si votre variable ne peut pas être déclarée finale ou si vous ne voulez pas la déclarer finale, essayez ceci:

final String[] helloworld = new String[1];
helloworld[0] = "Hello World!";
System.out.println(helloworld[0]);
helloworld[0] = "A String";
System.out.println(helloworld[0]);

Ceci imprimera:

Hello World!
A String
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top