Quel est l'intérêt de la «classe finale» en Java?


569

Je lis un livre sur Java et il dit que vous pouvez déclarer la classe entière comme final. Je ne peux penser à rien où j'utiliserais cela.

Je suis nouveau dans la programmation et je me demande si les programmeurs l'utilisent réellement dans leurs programmes . S'ils le font, quand l'utilisent-ils pour que je puisse mieux le comprendre et savoir quand l'utiliser.

Si Java est orienté objet et que vous déclarez une classe final, cela n'arrête-t-il pas l'idée que la classe ait les caractéristiques des objets?

Réponses:


531

Tout d'abord, je recommande cet article: Java: quand créer une classe finale


S'ils le font, quand l'utilisent-ils pour que je puisse mieux le comprendre et savoir quand l'utiliser.

Une finalclasse est simplement une classe qui ne peut pas être étendue .

(Cela ne signifie pas que toutes les références aux objets de la classe agiraient comme si elles étaient déclarées comme final.)

Quand il est utile de déclarer une classe comme finale est couvert dans les réponses à cette question:

Si Java est orienté objet et que vous déclarez une classe final, cela n'arrête-t-il pas l'idée que la classe ait les caractéristiques des objets?

Dans un certain sens, oui.

En marquant une classe comme finale, vous désactivez une fonctionnalité puissante et flexible du langage pour cette partie du code. Cependant, certaines classes ne devraient pas (et dans certains cas ne peuvent pas) être conçues pour prendre en compte le sous-classement dans le bon sens. Dans ces cas, il est logique de marquer la classe comme finale, même si cela limite la POO. (N'oubliez pas cependant qu'une classe finale peut encore prolonger une autre classe non finale.)


39
Pour ajouter à la réponse, l'un des principes de Java efficace est de privilégier la composition à l'héritage. L'utilisation du mot-clé final contribue également à faire respecter ce principe.
Riggy

9
"Vous le faites principalement pour des raisons d'efficacité et de sécurité." J'entends cette remarque assez souvent (même Wikipédia le dit) mais je ne comprends toujours pas le raisonnement derrière cet argument. Quelqu'un souhaite-t-il expliquer comment, par exemple, une chaîne java.lang.String non finale aurait pu être inefficace ou non sécurisée?
MRA

27
@MRA Si je crée une méthode qui accepte une chaîne comme paramètre, je suppose qu'elle est immuable, car les chaînes le sont. Par conséquent, je sais que je peux appeler n'importe quelle méthode sur l'objet String en toute sécurité et ne pas modifier la chaîne passée. Si je devais étendre String et changer l'implémentation de la sous-chaîne pour changer la chaîne réelle, alors l'objet String que vous attendiez immuable ne l'est plus.
Cruncher

1
@Sortofabeginner Et dès que vous dites que vous voulez que toutes les méthodes et champs String soient finaux, juste pour que vous puissiez créer une classe avec des fonctionnalités supplémentaires ... À ce stade, vous pourriez tout aussi bien créer une classe qui a une chaîne et créer des méthodes qui opèrent sur cette chaîne.
Cruncher

1
@Shay final (entre autres) est utilisé pour rendre un objet immuable, donc je ne dirais pas qu'ils n'ont rien à voir les uns avec les autres. Voir ici docs.oracle.com/javase/tutorial/essential/concurrency/…
Celeritas

184

En Java, les éléments avec le finalmodificateur ne peuvent pas être modifiés!

Cela inclut les classes finales, les variables finales et les méthodes finales:

  • Une classe finale ne peut être prolongée par aucune autre classe
  • Une variable finale ne peut pas être réaffectée une autre valeur
  • Une méthode finale ne peut pas être remplacée

40
La vraie question est pourquoi , pas quoi .
Francesco Menzani

8
L'instruction, "En Java, les éléments avec le finalmodificateur ne peuvent pas être modifiés!", Est trop catégorique et, en fait, pas entièrement correcte. Comme l'a dit Grady Booch, "Un objet a un état, un comportement et une identité". Bien que nous ne puissions pas changer l'identité d'un objet une fois que sa référence a été marquée comme finale, nous avons une chance de changer son état en attribuant de nouvelles valeurs à ses non- finalchamps (à condition, bien sûr, qu'il les ait.) Quiconque est l'intention d'obtenir une certification Oracle Java (comme 1Z0-808, etc.) devrait garder cela à l'esprit car il pourrait y avoir des questions sur cet aspect à l'examen ...
Igor Soudakevitch

33

Un scénario où final est important, lorsque vous souhaitez empêcher l'héritage d'une classe, pour des raisons de sécurité. Cela vous permet de vous assurer que le code que vous exécutez ne peut pas être remplacé par quelqu'un.

Un autre scénario est pour l'optimisation: je semble me souvenir que le compilateur Java inline certains appels de fonction des classes finales. Donc, si vous appelez a.x()et que a est déclaré final, nous savons au moment de la compilation quel sera le code et nous pouvons l'intégrer dans la fonction appelante. Je n'ai aucune idée si cela est réellement fait, mais avec le final c'est une possibilité.


7
L'inclusion est normalement effectuée uniquement par le compilateur juste à temps lors de l'exécution. Cela fonctionne aussi sans final, mais le compilateur JIT a un peu plus de travail à faire pour être certain qu'il n'y a pas de classes d'extension (ou que ces classes d'extension ne touchent pas à cette méthode).
Paŭlo Ebermann

Un bon article sur la question de l'inline et de l'optimisation peut être trouvé ici: lemire.me/blog/archives/2014/12/17/…
Josh Hemann

24

Le meilleur exemple est

public final class String

qui est une classe immuable et ne peut pas être étendue. Bien sûr, il n'y a pas que rendre la finale de la classe immuable.


Hehe, parfois, il protège les développeurs de Rube Goldergian d'eux-mêmes.
Zoidberg

16

Lecture pertinente: The Open-Closed Principle de Bob Martin.

Citation clé:

Les entités logicielles (classes, modules, fonctions, etc.) doivent être ouvertes pour l'extension, mais fermées pour la modification.

Le finalmot-clé est le moyen de l'imposer en Java, qu'il soit utilisé sur des méthodes ou sur des classes.


6
@Sean: Le déclarer ne finalrend- il pas la classe fermée pour l'extension plutôt que ouverte? Ou est-ce que je le prends trop à la lettre?
Goran Jovic

4
@Goran applique globalement la finale, oui. La clé est d'appliquer sélectivement la finale dans les endroits où vous ne voulez pas de modification (et bien sûr de fournir de bons crochets pour l'extension)
Sean Patrick Floyd

26
Dans OCP, "modification" fait référence à la modification du code source et "extension" fait référence à l'héritage de l'implémentation. Par conséquent, l'utilisation de finalsur une déclaration de classe / méthode n'aurait aucun sens si vous souhaitez que le code d'implémentation soit fermé pour modification mais ouvert pour extension par héritage.
Rogério

1
@Rogerio J'ai emprunté la référence (et l'interprétation) à la Spring Framework Reference (MVC) . À mon humble avis, cela a beaucoup plus de sens que la version originale.
Sean Patrick Floyd

L'extension est morte. Inutile. Décimé. Détruit. Je me fiche d'OCP. Il n'y a jamais d'excuse pour prolonger un cours.
Josh Woodcock

15

Si vous imaginez la hiérarchie des classes comme un arbre (comme c'est le cas en Java), les classes abstraites ne peuvent être que des branches et les classes finales sont celles qui ne peuvent être que des feuilles. Les classes qui n'appartiennent à aucune de ces catégories peuvent être à la fois des branches et des feuilles.

Il n'y a pas de violation des principes OO ici, le final fournit simplement une symétrie sympa.

En pratique, vous souhaitez utiliser final si vous voulez que vos objets soient immuables ou si vous écrivez une API, pour signaler aux utilisateurs de l'API que la classe n'est tout simplement pas destinée à être étendue.


13

Le mot final- clé lui - même signifie que quelque chose est définitif et n'est censé être modifié d'aucune façon. Si une classe est marquée, finalelle ne peut pas être étendue ou sous-classée. Mais la question est pourquoi marquons-nous une classe final? OMI, il existe plusieurs raisons:

  1. Normalisation: certaines classes exécutent des fonctions standard et ne sont pas censées être modifiées, par exemple les classes exécutant diverses fonctions liées aux manipulations de chaînes ou aux fonctions mathématiques, etc.
  2. Raisons de sécurité : Parfois, nous écrivons des classes qui exécutent diverses fonctions liées à l'authentification et au mot de passe et nous ne voulons pas qu'elles soient modifiées par quelqu'un d'autre.

J'ai entendu dire que la classe de marquage finalaméliore l'efficacité, mais franchement, je n'ai pas pu trouver cet argument de poids.

Si Java est orienté objet et que vous déclarez une classe finale, cela n'arrête-t-il pas l'idée que la classe ait les caractéristiques des objets?

Peut-être oui, mais parfois c'est le but recherché. Parfois, nous faisons cela pour obtenir de plus grands avantages de la sécurité, etc. en sacrifiant la capacité de cette classe à être étendue. Mais une classe finale peut encore étendre une classe si nécessaire.

D'un autre côté, nous devrions préférer la composition à l'héritage et le finalmot - clé aide réellement à faire respecter ce principe.


6

Soyez prudent lorsque vous faites un cours "final". Parce que si vous voulez écrire un test unitaire pour une classe finale, vous ne pouvez pas sous-classer cette classe finale afin d'utiliser la technique de rupture de dépendance "Subclass and Override Method" décrite dans le livre de Michael C. Feathers "Working Effectively with Legacy Code" . Dans ce livre, Feathers a déclaré: «Sérieusement, il est facile de croire que scellé et final sont une erreur erronée, qu'ils n'auraient jamais dû être ajoutés aux langages de programmation. Mais la vraie faute nous incombe. Lorsque nous dépendons directement de bibliothèques qui sont hors de notre contrôle, nous demandons juste des ennuis. "


6

final class peut éviter de casser l'API publique lorsque vous ajoutez de nouvelles méthodes

Supposons que sur la version 1 de votre Baseclasse vous fassiez:

public class Base {}

et un client fait:

class Derived extends Base {
    public int method() { return 1; }
}

Ensuite, si dans la version 2, vous souhaitez ajouter une methodméthode à Base:

class Base {
    public String method() { return null; }
}

cela casserait le code client.

Si nous l'avions utilisé à la final class Baseplace, le client n'aurait pas pu hériter et l'ajout de méthode ne casserait pas l'API.


5

Si la classe est marquée final, cela signifie que la structure de la classe ne peut pas être modifiée par quelque chose d'extérieur. Lorsque cela est le plus visible, c'est lorsque vous effectuez un héritage polymorphe traditionnel, class B extends Acela ne fonctionnera tout simplement pas. C'est essentiellement un moyen de protéger certaines parties de votre code (dans une large mesure) .

Pour clarifier, le marquage de classe finalne marque pas ses champs en tant finalque tels et ne protège donc pas les propriétés de l'objet mais la structure de classe réelle à la place.


1
Que signifient les propriétés de l'objet? Cela signifie-t-il que je pourrais modifier la variable membre de la classe si la classe est déclarée finale? Le seul but de la classe finale est donc d'empêcher l'héritage.
Adam Lyu

5

POUR RÉSOUDRE LE PROBLÈME DE LA CLASSE FINALE:

Il y a deux façons de faire une finale de classe. 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 privés:

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

Le marquer comme final vous évite les ennuis si vous découvrez qu'il s'agit bien d'une finale, pour démontrer le regard sur cette classe de test. semble public à première vue.

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

Malheureusement, comme le seul constructeur de la classe est privé, il est impossible d'étendre cette classe. Dans le cas de la classe Test, il n'y a aucune raison 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 la marquer comme finale lorsque vous créez implicitement une finale de classe en rendant son constructeur privé.


4

Une classe finale est une classe qui ne peut pas être étendue. Des méthodes peuvent également être déclarées comme finales pour indiquer qu'elles ne peuvent pas être remplacées par des sous-classes.

Il peut être particulièrement utile d'empêcher la classe d'être sous-classée si vous écrivez des API ou des bibliothèques et que vous souhaitez éviter d'être étendu pour modifier le comportement de la base.


4

Un avantage de garder une classe comme finale: -

La classe de chaîne est conservée finale afin que personne ne puisse remplacer ses méthodes et modifier la fonctionnalité. par exemple, personne ne peut changer la fonctionnalité de la méthode length (). Il renverra toujours la longueur d'une chaîne.

Le développeur de cette classe ne voulait que personne ne change la fonctionnalité de cette classe, il l'a donc gardée comme finale.


3

Oui, parfois, vous pouvez souhaiter cela, soit pour des raisons de sécurité ou de vitesse. Cela se fait également en C ++. Il ne peut pas être que applicable pour les programmes, mais plus encore pour les cadres. http://www.glenmccl.com/perfj_025.htm


3

En java, le mot clé final utilise pour les occasions ci-dessous.

  1. Variables finales
  2. Méthodes finales
  3. Classes finales

En java, les variables finales ne peuvent pas être réaffectées, les classes finales ne peuvent pas être étendues et les méthodes finales ne peuvent pas remplacer.


1

Les classes finales ne peuvent pas être prolongées. Donc, si vous voulez qu'une classe se comporte d'une certaine manière et que personne ne remplace les méthodes (avec éventuellement du code moins efficace et plus malveillant), vous pouvez déclarer la classe entière comme méthodes finales ou spécifiques que vous ne voulez pas être modifié.

Puisque déclarer une classe n'empêche pas une classe d'être instanciée, cela ne signifie pas qu'elle empêchera la classe d'avoir les caractéristiques d'un objet. C'est juste que vous devrez vous en tenir aux méthodes telles qu'elles sont déclarées dans la classe.


1

pensez à FINAL comme au «bout de la ligne» - ce type ne peut plus produire de progéniture. Donc, quand vous le voyez de cette façon, il y a des tonnes de scénarios du monde réel que vous rencontrerez qui vous obligeront à signaler un marqueur de fin de ligne à la classe. Il s'agit d'une conception pilotée par domaine - si votre domaine exige qu'une ENTITY (classe) donnée ne puisse pas créer de sous-classes, marquez-la comme FINALE.

Je dois noter que rien ne vous empêche d'hériter d'une classe "devrait être étiquetée comme finale". Mais cela est généralement classé comme «abus d'héritage», et cela est fait parce que le plus souvent, vous souhaitez hériter d'une fonction de la classe de base de votre classe.

La meilleure approche consiste à examiner le domaine et à le laisser dicter vos décisions de conception.


1

Comme indiqué ci-dessus, si vous voulez que personne ne puisse modifier la fonctionnalité de la méthode, vous pouvez la déclarer finale.

Exemple: chemin du fichier du serveur d'applications pour le téléchargement / téléchargement, fractionnement de la chaîne en fonction de l'offset, ces méthodes vous pouvez le déclarer final afin que ces fonctions de méthode ne soient pas modifiées. Et si vous voulez de telles méthodes finales dans une classe distincte, définissez cette classe comme classe finale. Ainsi, la classe finale aura toutes les méthodes finales, alors que la méthode finale peut être déclarée et définie dans la classe non finale.



1

Disons que vous avez une Employeeclasse qui a une méthode greet. Lorsque la greetméthode est appelée, elle s'imprime simplement Hello everyone!. Voilà donc le comportement attendu de la greetméthode

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

Maintenant, laissez la GrumpyEmployeesous - classe Employeeet remplacer la greetméthode comme indiqué ci-dessous.

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Maintenant, dans le code ci-dessous, jetez un œil à la sayHellométhode. Il prend l' Employeeinstance comme paramètre et appelle la méthode de bienvenue en espérant qu'elle dirait Hello everyone!Mais ce que nous obtenons est Get lost!. Ce changement de comportement est dû àEmployee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

Cette situation peut être évitée si le Employeecours a été fait final. Imaginez simplement le chaos qu'un programmeur effronté pourrait causer si StringClass n'était pas déclaré comme final.


1

La classe finale ne peut pas être prolongée davantage. Si nous n'avons pas besoin de rendre une classe héritable en java, nous pouvons utiliser cette approche.

Si nous avons juste besoin de créer des méthodes particulières dans une classe pour ne pas les remplacer, nous pouvons simplement mettre le mot-clé final devant elles. Là, la classe est toujours héritable.


-1

L'orientation d'objet n'est pas une question d'héritage, c'est une question d'encapsulation. Et l'héritage rompt l'encapsulation.

Déclarer une finale de classe est parfaitement logique dans de nombreux cas. Tout objet représentant une «valeur» comme une couleur ou une somme d'argent pourrait être définitif. Ils sont autonomes.

Si vous écrivez des bibliothèques, rendez vos classes finales à moins que vous ne les indentiez explicitement comme dérivées. Sinon, les gens peuvent dériver vos classes et remplacer les méthodes, brisant vos hypothèses / invariants. Cela peut également avoir des conséquences sur la sécurité.

Joshua Bloch dans «Effective Java» recommande de concevoir explicitement pour l'héritage ou de l'interdire et il note que la conception pour l'héritage n'est pas si simple.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.