web-dev-qa-db-fra.com

Utilisation du modificateur "final", le cas échéant, dans Java

En Java, il est pratique de déclarer chaque variable (locale ou classe), paramètre final si elles le sont réellement.

Bien que cela rend le code beaucoup plus verbeux, cela facilite la lecture/saisie du code et évite également les erreurs car l’intention est clairement indiquée.

Quelles sont vos pensées à ce sujet et que suivez-vous?

187
surajs

Je pense que tout cela a à voir avec un bon style de codage. Bien sûr, vous pouvez écrire de bons programmes robustes sans utiliser beaucoup de modificateurs final partout, mais quand vous y réfléchissez ...

Ajouter final à toutes les choses qui ne devraient pas changer simplement réduit les possibilités que vous (ou le prochain programmeur travaille sur votre code) mal interpréter ou mal utiliser le processus de pensée qui a abouti à votre code. Au moins, cela devrait vous rappeler quelque chose quand ils veulent maintenant changer ce que vous aviez auparavant immuable.

Au début, il semble assez bizarre de voir beaucoup de mots-clés final dans votre code, mais très vite, vous ne remarquerez plus le mot lui-même et vous penserez simplement que -Chose-ne-changera jamais à partir de ce point (vous pouvez me le prendre ;-)

Je pense que c'est une bonne pratique. Je ne l'utilise pas tout le temps, mais quand je peux et qu'il est logique d'étiqueter quelque chose, je le ferai final.

174
Johan Pelgrim

Obsess sur:

  • Champs finaux - Marquer les champs 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 comptez 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 le marquage des paramètres et des variables locales que je ne vais pas modifier est "plus juste". 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.
182
Alex Miller

Vous devez vraiment comprendre l'utilisation complète du mot clé final avant de l'utiliser. Il peut s’appliquer à et a des effets différents sur les variables, les champs, les méthodes et les classes

Je vous recommande de consulter l'article lié à ci-dessous pour plus de détails.

mot final sur le mot clé final

32
HowardSP

Le modificateur final, en particulier pour les variables, permet au compilateur d'appliquer une convention généralement judicieuse: assurez-vous qu'une variable (locale ou instance) est affectée exactement une fois (ni plus ni moins). En vous assurant qu'une variable est définitivement affectée avant son utilisation, vous pouvez éviter les cas courants de NullPointerException:

final FileInputStream in;
if(test)
  in = new FileInputStream("foo.txt");
else
  System.out.println("test failed");
in.read(); // Compiler error because variable 'in' might be unassigned

En empêchant l'affectation d'une variable plus d'une fois, vous découragez la portée trop étendue. Au lieu de cela:

 String msg = null;
 for(int i = 0; i < 10; i++) {
     msg = "We are at position " + i;
     System.out.println(msg);
 }
 msg = null;

Vous êtes encouragé à utiliser ceci:

 for(int i = 0; i < 10; i++) {
     final String msg = "We are at position " + i;
     System.out.println(msg);
 }

Quelques liens:

29
Bruno De Fraine

Je suis plutôt dogmatique à propos de déclarer toutes les variables possibles final. Cela inclut les paramètres de méthode, les variables locales et, rarement, les champs d'objet de valeur. J'ai trois raisons principales pour déclarer des variables finales partout:

  1. Déclaration d'intention: En déclarant une variable finale, j'affirme que cette variable est destinée à être écrite dans une seule fois. C'est un indice subtil pour les autres développeurs et un indice important pour le compilateur.
  2. Application des variables à usage unique: Je crois en l'idée que chaque variable ne devrait avoir qu'un seul but dans la vie. En attribuant à chaque variable un seul objectif, vous réduisez le temps nécessaire pour définir l'objectif de cette variable lors du débogage.
  3. Permet l'optimisation: Je sais que le compilateur avait des astuces d'amélioration des performances qui reposaient spécifiquement sur l'immuabilité d'une référence de variable. J'aime penser que certaines de ces vieilles astuces de performance (ou de nouvelles) seront utilisées par le compilateur.

Cependant, je pense que les classes et méthodes finales ne sont pas aussi utiles que les références de variables finales. Le mot-clé final, lorsqu'il est utilisé avec ces déclarations, fournit simplement des obstacles au test automatisé et à l'utilisation de votre code d'une manière que vous n'auriez jamais pu anticiper.

20
Ryan Ransford

Effective Java contient un élément qui dit "Favoriser les objets immuables". Déclarer les champs en tant que final vous aide à faire quelques petits pas dans cette direction, mais il existe bien sûr beaucoup plus que des objets véritablement immuables.

Si vous savez que les objets sont immuables, vous pouvez les partager avec de nombreux threads/clients sans soucis de synchronisation, et il est plus facile de raisonner sur le fonctionnement du programme.

17
Lars Westergren

Je n'ai jamais été dans une situation où le dernier mot-clé associé à une variable m'a empêché de commettre une erreur. Pour l'instant, je pense que c'est une perte de temps énorme.

À moins qu'il y ait une vraie raison de le faire (car vous voulez faire valoir un point spécifique sur le fait que cette variable est finale), je préférerais ne pas le faire car je trouve que cela rend le code moins lisible.

Si, toutefois, vous ne trouvez pas que cela rend le code plus difficile à lire ou plus long à écrire, alors foncez.

Edit: Pour clarifier (et tenter de gagner des votes à la baisse), je ne dis pas que les constantes ne sont pas marquées comme définitives, mais que vous ne faites pas des choses comme:

public String doSomething() {
  final String first = someReallyComplicatedExpressionToGetTheString();
  final String second = anotherReallyComplicatedExpressionToGetAnother();

  return first+second;
}

Cela rend simplement le code (à mon avis) plus difficile à lire.

Il convient également de rappeler que all final ne vous empêche pas de réaffecter une variable, cela ne la rend pas immuable ou quelque chose du genre.

10
SCdF

Final doit toujours être utilisé pour les constantes. C'est même utile pour les variables de courte durée (au sein d'une seule méthode) lorsque les règles de définition de la variable sont compliquées.

Par exemple:

final int foo;
if (a)
    foo = 1;
else if (b)
    foo = 2;
else if (c)
    foo = 3;
if (d)        // Compile error:  forgot the 'else'
    foo = 4;
else
    foo = -1;
8
David Leppik

Cela ressemble à l’un des plus gros arguments contre en utilisant le mot-clé final, c’est que "c’est inutile" et que cela "gaspille de l’espace".

Si nous reconnaissons les nombreux avantages de "final", comme le soulignent de nombreux excellents articles, tout en admettant que cela prend plus de frappe et d'espace, je dirais que Java aurait dû créer des variables "final" par par défaut, et exigent que les choses soient marquées "mutable" si le codeur le veut.

5
RAY

J'utilise final tout le temps pour les attributs d'objet.

Le mot clé final a une sémantique de visibilité lorsqu'il est utilisé sur des attributs d'objet. Fondamentalement, la définition de la valeur d'un attribut d'objet final se produit avant le retour du constructeur. Cela signifie que tant que vous ne laissez pas la référence this échapper au constructeur, vous utilisez final pour all vous attribuez, votre objet est (sous Java 5 sémantique) garanti pour être correctement construit, et comme il est également immuable, il peut être publié en toute sécurité sur d'autres threads.

Les objets immuables ne concernent pas uniquement la sécurité des threads. Ils facilitent également la tâche de raisonner sur les transitions d’états dans votre programme, car l’espace de ce que peut modifier est délibérément et, s’il est utilisé de manière cohérente, complètement limité aux seules choses qui devraient changer.

Je fais aussi parfois des méthodes finales, mais pas aussi souvent. Je fais rarement des cours en finale. Je le fais généralement parce que j'en ai peu besoin. Je n'utilise généralement pas beaucoup l'héritage. Je préfère utiliser des interfaces et la composition d'objet à la place - cela se prête également à une conception que je trouve souvent plus facile à tester. Lorsque vous codez pour des interfaces au lieu de classes concrètes, vous n'avez pas besoin d'utiliser l'héritage lorsque vous testez tel quel avec des frameworks tels que jMock , beaucoup plus facile de créer des objets fictifs avec des interfaces qu'avec des classes concrètes.

Je suppose que je devrais faire la majorité de mes cours en finale, mais je ne suis pas encore entré dans l'habbit.

5
Chris Vest

Je dois lire beaucoup de code pour mon travail. L’absence de variables finales sur une instance est l’un des principaux problèmes qui me dérangent et qui compliquent inutilement la compréhension du code. Pour mon argent, final sur les variables locales provoque plus de fouillis que de clarté. Le langage aurait dû être conçu pour en faire la valeur par défaut, mais nous devons vivre avec l'erreur. Parfois, cela est particulièrement utile avec des boucles et une affectation définie avec un arbre if-else, mais cela tend généralement à indiquer que votre méthode est trop compliquée.

4

final devrait évidemment être utilisé sur des constantes et pour imposer l'immuabilité, mais il existe un autre usage important pour les méthodes.

Effective Java contient un élément entier sur ce point (élément 15), soulignant les pièges d'un héritage non prévu. Effectivement, si vous n'avez pas conçu et documenté votre classe pour l'héritage, hériter de celle-ci peut donner des problèmes inattendus (l'élément en est un bon exemple). La recommandation est donc que vous utilisiez final sur toute classe et/ou méthode dont il n'était pas destiné à être hérité.

Cela peut sembler draconien, mais c'est logique. Si vous écrivez une bibliothèque de classes pour une utilisation par d'autres, vous ne voulez pas qu'elles héritent d'éléments non conçus pour elle. Vous allez vous enfermer dans une implémentation particulière de la classe pour assurer la compatibilité avec les versions antérieures. Si vous codez dans une équipe, rien ne peut empêcher un autre membre de l’équipe de supprimer le final s’il le faut vraiment. Mais le mot-clé leur fait penser à ce qu’ils font et les avertit que la classe dont ils héritent n’a pas été conçue pour cela. Ils doivent donc redoubler de prudence.

3
DJClayworth

Un autre inconvénient est que beaucoup de gens confondent final avec le sens que le contenu de la variable d'instance ne peut pas changer, et que la référence ne peut pas changer.

3
Uri

Choisir de taper final pour chaque paramètre de chaque méthode produira beaucoup d’irritation à la fois pour les codeurs et les lecteurs de code.

Une fois que l'irritation dépasse le seuil raisonnable, passez à Scala où les arguments sont finaux par défaut).

Ou, vous pouvez toujours utiliser des outils de style de code qui le feront automatiquement pour vous. Tous les IDE les ont implémentés ou en tant que plugins.

2
Oleg Mikheev

Même pour les variables locales, sachant que celle-ci est déclarée finale signifie que je n'ai pas à craindre que la référence soit modifiée ultérieurement. Cela signifie que lors du débogage et que je vois cette variable plus tard, je suis convaincu que cela fait référence au même objet. C'est une chose de moins dont je dois m'inquiéter lorsque je cherche un bug. Un bonus est que si 99% des variables sont déclarées finales, les quelques variables qui sont réellement variables se démarquent mieux. De plus, la finale permet au compilateur de trouver d'autres erreurs stupides possibles qui pourraient autrement passer inaperçues.

2
Diastrophism

Je code depuis un moment et j'utilise final quand je peux. Après avoir effectué cela pendant un certain temps (pour les variables, les paramètres de méthode et les attributs de classe), je peux dire que 90% (ou plus) de mes variables sont en réalité définitives. Je pense que l’avantage de ne PAS modifier les variables lorsque vous ne le souhaitez pas (j’ai déjà vu cela et c’est parfois pénible) paie pour la frappe supplémentaire et les mots-clés "finaux" supplémentaires de votre code.

Cela étant dit, si je concevais un langage, je rendrais chaque variable finale sauf si elle était modifiée par un autre mot clé.

Je n'utilise pas beaucoup final pour les cours et les méthodes, pensa. C'est un choix de conception plus ou moins compliqué, à moins que votre classe ne soit une classe d'utilitaires (dans ce cas, vous ne devriez avoir qu'un seul constructeur privé).

J'utilise également Collections.unmodifiable ... pour créer des listes non modifiables lorsque j'en ai besoin.

1
Ravi Wallau

Pour les arguments, je pense qu'ils ne sont pas nécessaires. Mostley, ils ont juste blessé la facilité de lecture. Réaffecter une variable argument est si incroyablement stupide que je devrais être assez confiant qu’elles peuvent être traitées comme des constantes de toute façon.

Le fait qu'Eclipse colore le rouge final facilite l'identification des déclarations de variables dans le code, ce qui, à mon avis, améliore la capacité de lecture le plus souvent.

J'essaie d'appliquer la règle selon laquelle toutes les variables doivent être définitives s'il n'y a pas de raison valable de ne pas le faire. Il est tellement plus facile de répondre à la question "quelle est cette variable?" question si vous avez juste à trouver l’initiation et à être sûr que c’est tout.

En fait, je deviens plutôt nerveux au sujet des variables non finales maintenant plusieurs jours. C'est comme la différence entre avoir un couteau pendu à un fil, ou simplement l'avoir dans un tiroir de cuisine ...

Une dernière variable n’est qu’un bon moyen de définir les valeurs.

Une variable non finale est liée à une partie d'un algorithme sujet à des bogues.

Une caractéristique intéressante de Nice est que lorsque l’option d’utiliser une variable hors de propos pour un algorithme la plupart du temps, la solution est d’écrire une méthode à la place, ce qui améliore généralement considérablement le code.

1
John Nilsson

J'ai configuré Eclipse pour ajouter final sur tous les champs et attributs non modifiés. Cela fonctionne bien en utilisant les "actions de sauvegarde" Eclipse qui ajoutent ces derniers modificateurs (entre autres) lors de la sauvegarde du fichier.

Hautement recommandé.

Découvrez mon blog de Eclipse Save Actions.

1
zvikico

Je ne les utilise jamais sur des variables locales, il y a peu d'intérêt pour la verbosité ajoutée. Même si vous pensez que la variable ne devrait pas être réaffectée, cela ne changera pas grand chose à la prochaine personne qui modifiera le code qui pense le contraire et, étant donné que le code est modifié, tout objectif initial de le rendre final peut ne plus être valide. Si c’est juste pour plus de clarté, j’estime que cela échoue à cause des effets négatifs de la verbosité.

Il en va à peu près de même pour les variables membres, car elles offrent peu d’avantages, sauf dans le cas des constantes.

Cela n'a pas non plus d'incidence sur l'immutabilité, car le meilleur indicateur de l'immuabilité est qu'elle est documentée en tant que telle et/ou ne dispose d'aucune méthode qui puisse altérer l'objet (ceci, en plus de rendre la classe finale est le seul moyen de garantir que c'est immuable).

Mais bon, c'est juste mon avis :-)

1
Robin

J'utilise rarement les méthodes ou les classes de finales car j'aime permettre aux gens de les remplacer.

Sinon, je n'utilise finalement que s'il s'agit d'un public/private static final type SOME_CONSTANT;

1
jjnguy

Final lorsqu'il est utilisé avec des variables dans Java fournit un substitut à la constante en C++. Ainsi, lorsque final et statique sont utilisés pour une variable, ils deviennent immuables. En même temps, les programmeurs C++ migrés sont très heureux; - )

Utilisé avec des variables de référence, il ne vous permet pas de re-référencer l'objet, bien que celui-ci puisse être manipulé.

Lorsque final est utilisé avec une méthode, cela ne permet pas à la méthode d'être écrasée par les sous-classes.

Une fois que l'utilisation est très claire, il convient de l'utiliser avec précaution. Cela dépend principalement de la conception, car l’utilisation de final sur la méthode n’aiderait pas le polymorphisme.

Vous ne devriez l'utiliser que pour les variables lorsque vous êtes absolument certain que la valeur de la variable ne sera/ne sera jamais modifiée. Veillez également à suivre la convention de codage préconisée par Sun.for, par exemple: final int COLOR_RED = 1; (Majuscules séparées par un trait de soulignement)

Avec une variable de référence, utilisez-la uniquement lorsque vous avez besoin d'une référence immuable à un objet particulier.

En ce qui concerne la partie lisibilité, il est évident que les commentaires jouent un rôle très important lors de l’utilisation du modificateur final.

1
Omnipotent

Le marquage de la classe finale peut également faire en sorte que certaines liaisons de méthodes se produisent lors de la compilation au lieu de l'exécution. Considérez "v2.foo ()" ci-dessous - le compilateur sait que B ne peut pas avoir de sous-classe. Par conséquent, foo () ne peut pas être remplacé afin que l'implémentation à appeler soit connue au moment de la compilation. Si la classe B n'est PAS marquée comme finale, il est alors possible que le type réel de v2 soit une classe qui étend B et remplace foo ().

class A {
    void foo() {
        //do something
    }
}
final class B extends A {
    void foo() {
    }
}
class Test {
    public void t(A v1, B v2) {
        v1.foo();
        v2.foo();
    }
}
0
Eugene

L'utilisation de classes locales anonymes pour les écouteurs d'événements est un modèle courant en Java. L'utilisation la plus courante du mot-clé final est de s'assurer que les variables de la portée sont accessibles au même écouteur.

Cependant, si vous vous trouvez obligé de mettre beaucoup de déclarations finales dans votre code. Cela pourrait être un bon indice que vous faites quelque chose de mal.

L'article posté ci-dessus donne cet exemple:

public void doSomething(int i, int j) {
    final int n = i + j; // must be declared final

    Comparator comp = new Comparator() {
        public int compare(Object left, Object right) {
            return n; // return copy of a local variable
        }
    };
}
0
Hans Sjunnesson

Je l'utilise pour les constantes à l'intérieur et à l'extérieur des méthodes.

Je ne l'utilise parfois que pour les méthodes, car je ne sais pas si une sous-classe NE voudrait PAS remplacer une méthode donnée (pour quelque raison que ce soit).

En ce qui concerne les classes, uniquement pour certaines classes d'infrastructure, j'ai utilisé la classe finale.

IntelliJ IDEA vous avertit si un paramètre de fonction est écrit dans une fonction. J'ai donc arrêté d'utiliser final pour les arguments de la fonction. Je ne les vois pas à l'intérieur Java Bibliothèque d'exécution également.

0
anjanb