Le mot-clé «final» en Java

1. Vue d'ensemble

Alors que l'héritage nous permet de réutiliser le code existant, nous devons parfois définir des limites d'extensibilité pour diverses raisons; le mot-clé final nous permet de faire exactement cela.

Dans ce didacticiel, nous examinerons ce que signifie le mot-clé final pour les classes, les méthodes et les variables.

2. Classes finales

Les cours marqués comme finaux ne peuvent pas être prolongés. Si nous regardons le code des bibliothèques Java Core, nous y trouverons de nombreuses classes finales . Un exemple est la classe String .

Considérez la situation si nous pouvons étendre la classe String , remplacer l'une de ses méthodes et remplacer toutes les instances String par les instances de notre sous-classe String spécifique .

Le résultat des opérations sur les objets String deviendra alors imprévisible. Et étant donné que la classe String est utilisée partout, c'est inacceptable. C'est pourquoi la classe String est marquée comme finale .

Toute tentative d'hériter d'une classe finale provoquera une erreur du compilateur. Pour démontrer cela, créons la classe finale Cat :

public final class Cat { private int weight; // standard getter and setter }

Et essayons de l'étendre:

public class BlackCat extends Cat { }

Nous verrons l'erreur du compilateur:

The type BlackCat cannot subclass the final class Cat

Notez que le mot-clé final dans une déclaration de classe ne signifie pas que les objets de cette classe sont immuables . Nous pouvons modifier les champs de l' objet Cat librement:

Cat cat = new Cat(); cat.setWeight(1); assertEquals(1, cat.getWeight()); 

Nous ne pouvons tout simplement pas le prolonger.

Si nous suivons strictement les règles de bonne conception, nous devons créer et documenter soigneusement une classe ou la déclarer définitive pour des raisons de sécurité. Cependant, nous devons faire preuve de prudence lors de la création des classes finales .

Notez que rendre une classe définitive signifie qu'aucun autre programmeur ne peut l'améliorer. Imaginez que nous utilisons une classe et que nous n'avons pas le code source, et qu'il y a un problème avec une méthode.

Si la classe est finale, nous ne pouvons pas l'étendre pour remplacer la méthode et résoudre le problème. En d'autres termes, nous perdons l'extensibilité, l'un des avantages de la programmation orientée objet.

3. Méthodes finales

Les méthodes marquées comme finales ne peuvent pas être remplacées. Lorsque nous concevons une classe et estimons qu'une méthode ne doit pas être remplacée, nous pouvons rendre cette méthode définitive . Nous pouvons également trouver de nombreuses méthodes finales dans les bibliothèques Java Core.

Parfois, nous n'avons pas besoin d'interdire complètement une extension de classe, mais seulement d'empêcher le remplacement de certaines méthodes. Un bon exemple de ceci est la classe Thread . Il est légal de l'étendre et de créer ainsi une classe de thread personnalisée. Mais ses méthodes isAlive () sont définitives .

Cette méthode vérifie si un thread est actif. Il est impossible de remplacer correctement la méthode isAlive () pour de nombreuses raisons. L'un d'eux est que cette méthode est native. Le code natif est implémenté dans un autre langage de programmation et est souvent spécifique au système d'exploitation et au matériel sur lequel il s'exécute.

Créons une classe Dog et rendons sa méthode sound () définitive :

public class Dog { public final void sound() { // ... } }

Étendons maintenant la classe Dog et essayons de remplacer sa méthode sound () :

public class BlackDog extends Dog { public void sound() { } }

Nous verrons l'erreur du compilateur:

- overrides com.baeldung.finalkeyword.Dog.sound - Cannot override the final method from Dog sound() method is final and can’t be overridden

Si certaines méthodes de notre classe sont appelées par d'autres méthodes, nous devrions envisager de rendre les méthodes appelées finales . Sinon, les ignorer peut affecter le travail des appelants et entraîner des résultats surprenants.

Si notre constructeur appelle d'autres méthodes, nous devons généralement déclarer ces méthodes finales pour la raison ci-dessus.

Quelle est la différence entre rendre toutes les méthodes de la classe finales et marquer la classe elle-même comme finale ? Dans le premier cas, nous pouvons étendre la classe et y ajouter de nouvelles méthodes.

Dans le second cas, nous ne pouvons pas faire cela.

4. Variables finales

Les variables marquées comme définitives ne peuvent pas être réaffectées. Une fois qu'une variable finale est initialisée, elle ne peut pas être modifiée.

4.1. Variables primitives finales

Déclarons une variable finale primitive i, puis affectons-lui 1.

Et essayons de lui attribuer une valeur de 2:

public void whenFinalVariableAssign_thenOnlyOnce() { final int i = 1; //... i=2; }

Le compilateur dit:

The final local variable i may already have been assigned

4.2. Variables de référence finales

Si nous avons une variable de référence finale , nous ne pouvons pas non plus la réaffecter. Mais cela ne veut pas dire que l'objet auquel il fait référence est immuable . Nous pouvons modifier librement les propriétés de cet objet.

Pour démontrer cela, déclarons la variable de référence finale cat et initialisons-la:

final Cat cat = new Cat();

Si nous essayons de le réaffecter, nous verrons une erreur du compilateur:

The final local variable cat cannot be assigned. It must be blank and not using a compound assignment

Mais nous pouvons changer les propriétés de l' instance Cat :

cat.setWeight(5); assertEquals(5, cat.getWeight());

4.3. Champs finaux

Les champs finaux peuvent être des constantes ou des champs à écriture unique. Pour les distinguer, nous devrions nous poser une question - inclurions-nous ce champ si nous devions sérialiser l'objet? Si non, cela ne fait pas partie de l'objet, mais une constante.

Notez que selon les conventions de dénomination, les constantes de classe doivent être en majuscules, avec des composants séparés par des traits de soulignement («_»):

static final int MAX_WIDTH = 999;

Notez que tout champ final doit être initialisé avant la fin du constructeur .

Pour les champs finaux statiques , cela signifie que nous pouvons les initialiser:

  • sur déclaration comme indiqué dans l'exemple ci-dessus
  • dans le bloc d'initialisation statique

Par exemple les champs finaux , cela signifie que nous pouvons les initialiser:

  • sur déclaration
  • dans le bloc d'initialisation d'instance
  • dans le constructeur

Sinon, le compilateur nous donnera une erreur.

4.4. Arguments finaux

Le mot-clé final peut également être placé avant les arguments de méthode. Un dernier argument ne peut pas être modifié dans une méthode :

public void methodWithFinalArguments(final int x) { x=1; }

L'affectation ci-dessus provoque l'erreur du compilateur:

The final local variable x cannot be assigned. It must be blank and not using a compound assignment

5. Conclusion

Dans cet article, nous avons appris ce que signifie le mot-clé final pour les classes, les méthodes et les variables. Bien que nous n'utilisions pas souvent le mot clé final dans notre code interne, cela peut être une bonne solution de conception.

Comme toujours, le code complet de cet article se trouve dans le projet GitHub.