ProgrammationDéveloppeur Java

Comment fonctionne le mécanisme final en Java — pour les variables, les méthodes et les classes, et quels effets inattendus peut provoquer une mauvaise utilisation de ce modificateur ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

final en Java est utilisé pour :

  • Variables — devient immuable après initialisation ;
  • Méthodes — ne peut pas être redéfini dans les sous-classes ;
  • Classes — ne peut pas être étendu.

Particularités et subtilités :

  • La variable final doit être initialisée lors de la déclaration ou dans le constructeur ;
  • La référence final n'autorise pas à changer la référence, mais l'objet référencé peut être modifié (s'il n'est pas immutable !) ;
  • L'héritage d'une classe final (par exemple, String, Math) est impossible ;
  • Impossible de redéfinir une méthode final, même si la classe est héritée.
final class A {} // impossible de faire class B extends A class Parent { final void foo() { } } class Child extends Parent { // void foo() {} // erreur : impossible de redéfinir foo } final int COUNT = 10;

Une question piégée.

Peut-on modifier l'état d'un objet auquel une variable final référence ?

Réponse : Oui, si c'est un objet non-immutable. Par exemple :

final List<String> names = new ArrayList<>(); names.add("Vasya"); // C'est autorisé — l'objet référencé change, mais pas la référence elle-même names = new ArrayList<>(); // Erreur de compilation

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet.


Histoire

Dans le système de journalisation, un unique final Logger était transmis dans tout le code, pensant que l'objet était complètement protégé – mais les réglages étaient modifiés via une méthode publique, brisant la configuration de manière globale.

Histoire

Dans une classe utilitaire, des champs final de type List ont été créés, puis ont tenté d'être retournés vers l'extérieur pour un usage commun. Le code extérieur a pu facilement modifier le contenu de la liste via la référence obtenue — final ne protège pas contre cela.

Histoire

Une tâche est apparue pour étendre une API externe basée sur des classes final. À cause d'elles, le faire s'est avéré impossible, ce qui a obligé à dupliquer la logique et à maintenir deux branches de produit indépendantes, compliquant ainsi les migrations.