Quelles sont les bonnes raisons d'interdire l'héritage en Java, par exemple en utilisant des classes finales ou des classes utilisant un seul constructeur privé sans paramètre? Quelles sont les bonnes raisons de rendre une méthode définitive?
Quelles sont les bonnes raisons d'interdire l'héritage en Java, par exemple en utilisant des classes finales ou des classes utilisant un seul constructeur privé sans paramètre? Quelles sont les bonnes raisons de rendre une méthode définitive?
Réponses:
Votre meilleure référence ici est l'article 19 de l'excellent livre de Joshua Bloch "Effective Java", intitulé "Conception et document pour l'héritage ou bien l'interdire". (C'est le point 17 dans la deuxième édition et le point 15 dans la première édition.) Vous devriez vraiment le lire, mais je vais résumer.
L'interaction des classes héritées avec leurs parents peut être surprenante et imprévisible si l'ancêtre n'a pas été conçu pour être hérité.
Les classes devraient donc être de deux types:
Classes conçues pour être étendues et avec suffisamment de documentation pour décrire comment cela doit être fait
Classes marquées comme finales
Si vous écrivez du code purement interne, cela peut être un peu excessif. Cependant, l'effort supplémentaire impliqué dans l'ajout de cinq caractères à un fichier de classe est très faible. Si vous écrivez uniquement pour la consommation interne, alors un futur codeur peut toujours supprimer le 'final' - vous pouvez le considérer comme un avertissement disant "cette classe n'a pas été conçue avec l'héritage à l'esprit".
Vous souhaiterez peut-être rendre une méthode finale afin que les classes de substitution ne puissent pas changer le comportement qui est compté dans d'autres méthodes. Les méthodes appelées dans les constructeurs sont souvent déclarées finales afin que vous n'ayez pas de mauvaises surprises lors de la création d'objets.
final
.
Une des raisons de rendre une classe définitive serait si vous vouliez forcer la composition sur l'héritage. Ceci est généralement souhaitable pour éviter un couplage étroit entre les classes.
Il existe 3 cas d'utilisation où vous pouvez utiliser les méthodes finales.
Objectif pour rendre une classe finale:
Pour qu'aucun organisme ne puisse étendre ces classes et modifier leur comportement.
Exemple: classe Wrapper Integer est une classe finale. Si cette classe n'est pas définitive, alors n'importe qui peut étendre Integer dans sa propre classe et changer le comportement de base de la classe entière. Pour éviter cela, java a créé toutes les classes wrapper en tant que classes finales.
DerivedInteger
, elle ne change toujours pas la classe Integer d'origine, et quiconque l'utilise le DerivedInteger
fait à ses risques et périls, donc je ne comprends toujours pas pourquoi c'est un problème.
vous voudrez peut-être créer des objets immuables ( http://en.wikipedia.org/wiki/Immutable_object ), vous voudrez peut-être créer un singleton ( http://en.wikipedia.org/wiki/Singleton_pattern ), ou vous voudrez peut-être pour empêcher quelqu'un de passer outre la méthode pour des raisons d'efficacité, de sûreté ou de sécurité.
L'héritage est comme une tronçonneuse - très puissante, mais horrible entre de mauvaises mains. Soit vous concevez une classe dont vous héritez (ce qui peut limiter la flexibilité et prendre beaucoup plus de temps), soit vous devez l'interdire.
Voir les articles 16 et 17 de la 2e édition effective de Java, ou mon article de blog "Inheritance Tax" .
Hmmm ... je peux penser à deux choses:
Vous pourriez avoir une classe qui traite de certains problèmes de sécurité. En le sous-classant et en alimentant votre système dans la version sous-classée de celui-ci, un attaquant peut contourner les restrictions de sécurité. Par exemple, votre application peut prendre en charge des plugins et si un plugin peut simplement sous-classer vos classes pertinentes pour la sécurité, il peut utiliser cette astuce pour en quelque sorte faire passer une version sous-classée de celui-ci en place. Cependant, c'est plutôt quelque chose dont Sun doit faire face en ce qui concerne les applets et autres, peut-être pas un cas aussi réaliste.
Une solution beaucoup plus réaliste consiste à éviter qu'un objet ne devienne mutable. Par exemple, puisque les chaînes sont immuables, votre code peut en conserver les références en toute sécurité
String blah = someOtherString;
au lieu de copier d'abord la chaîne. Cependant, si vous pouvez sous-classer String, vous pouvez y ajouter des méthodes qui permettent de modifier la valeur de la chaîne, maintenant aucun code ne peut plus compter sur le fait que la chaîne restera la même si elle copie simplement la chaîne comme ci-dessus, à la place, il doit dupliquer le chaîne.
De plus, si vous écrivez une classe de source fermée commerciale, vous ne voudrez peut-être pas que les gens puissent changer la fonctionnalité en bout de ligne, surtout si vous avez besoin de la soutenir et que les gens ont remplacé votre méthode et se plaignent que son appel donne des résultats inattendus.
Si vous marquez les classes et les méthodes comme finales, vous remarquerez peut-être un petit gain de performances, car le runtime n'a pas besoin de rechercher la bonne méthode de classe à appeler pour un objet donné. Les méthodes non finales sont marquées comme virtuelles afin de pouvoir être correctement étendues si nécessaire, les méthodes finales peuvent être directement liées ou compilées en ligne dans la classe.
Pour empêcher les gens de faire des choses qui pourraient confondre eux-mêmes et les autres. Imaginez une bibliothèque de physique où vous avez des constantes ou des calculs définis. Sans utiliser le mot-clé final, quelqu'un pourrait venir redéfinir des calculs de base ou des constantes qui ne devraient JAMAIS changer.
Vous souhaitez rendre une méthode définitive afin que le remplacement des classes ne modifie pas son comportement. Lorsque vous souhaitez pouvoir modifier le comportement, rendez la méthode publique. Lorsque vous remplacez une méthode publique, elle peut être modifiée.