En parcourant le Java Collections Framework, j'ai remarqué que bon nombre des interfaces ont le commentaire (optional operation)
. Ces méthodes permettent à des classes d'implémentation de passer par UnsupportedOperationException
si elles ne veulent tout simplement pas implémenter cette méthode.
Un exemple de ceci est la addAll
méthode dans le Set Interface
.
Maintenant, comme indiqué dans cette série de questions, les interfaces sont un contrat déterminant pour ce que l'utilisation peut en attendre.
Les interfaces sont importantes car elles séparent ce que fait une classe de la façon dont elle le fait. Le contrat définissant ce à quoi un client peut s'attendre laisse le développeur libre de l'implémenter comme bon lui semble, tant qu'il respecte le contrat.
et
Une interface est une description des actions qu'un objet peut effectuer ... par exemple, lorsque vous actionnez un interrupteur d'éclairage, la lumière s'allume, vous ne vous souciez pas de savoir comment, tout simplement. Dans la programmation orientée objet, une interface est une description de toutes les fonctions qu'un objet doit avoir pour être un "X".
et
Je pense que l'approche basée sur l'interface est beaucoup plus agréable. Vous pouvez ensuite bien simuler vos dépendances, et tout est fondamentalement moins étroitement couplé.
Quel est l'intérêt d'une interface?
Interface + extension (mixin) vs classe de base
Étant donné que le but des interfaces est de définir un contrat et de coupler vos dépendances de manière lâche, le fait que certaines méthodes ne vident pas en quelque UnsupportedOperationException
sorte le but? Cela signifie que je ne peux plus passer Set
et simplement utiliser addAll
. Au contraire, je dois savoir quelle implémentation de Set
j'ai été adoptée, donc je peux savoir si je peux l'utiliser addAll
ou non. Cela me semble assez inutile.
Alors à quoi ça sert UnsupportedOperationException
? Est-ce juste pour compenser le code hérité, et ils doivent nettoyer leurs interfaces? Ou a-t-il un objectif plus sensé qui me manque?
la source
addAll
dansHashSet
. Il s'en remet à l'implémentation par défaut dansAbstractCollection
laquelle ne jette certainement pasUnsupportedOperationException
.src.zip
il fonctionne très bien. Cela aide à savoir exactement quel code le JRE exécute parfois et à ne pas s'en remettre au JavaDoc qui peut être un peu bavard.Réponses:
Regardez les interfaces suivantes:
Ces interfaces déclarent toutes les méthodes de mutation comme facultatives. Cela documente implicitement le fait que la classe Collections est capable de renvoyer des implémentations de ces interfaces qui sont immuables: c'est-à-dire que ces opérations de mutation facultatives sont garanties d'échouer. Cependant, selon le contrat dans JavaDoc, toutes les implémentations de ces interfaces doivent autoriser les opérations de lecture. Cela inclut les implémentations "normales" telles que
HashSet
etLinkedList
ainsi que les wrappers immuables dansCollections
.Contraste avec les interfaces de file d'attente:
Ces Interface ne pas spécifier les opérations optionnelles: une file d' attente, par définition, est conçu pour des éléments d'offre et sondage de manière FIFO. Une file d'attente immuable est à peu près aussi utile qu'une voiture sans roues.
Une idée courante qui revient à plusieurs reprises est d'avoir une hiérarchie d'héritage qui a des objets mutables et immuables. Cependant, ceux-ci ont tous des inconvénients. La complexité brouille les eaux sans vraiment résoudre le problème.
Une hypothétique
Set
pourrait avoir les opérations de lecture et une sous-interfaceMutableSet
pourrait avoir les opérations d'écriture. Liskov nous dit qu'un aMutableSet
pourrait alors être transmis à tout ce qui a besoin d'unSet
. Au début, cela semble correct, mais considérons une méthode qui s'attend à ce que l'ensemble sous-jacent ne soit pas modifié pendant la lecture: il serait possible pour deux threads d'utiliser le même ensemble et de violer l'invariant de l'ensemble qui ne change pas. Cela pourrait causer un problème, par exemple si une méthode lit un élément de l'ensemble deux fois et qu'il est là la première fois mais pas la deuxième fois.Set
pourrait ne pas avoir d'implémentations directes, mais avoirMutableSet
etImmutableSet
comme sous-interfaces qui sont ensuite utilisées pour implémenter des classes. Cela a le même problème que ci-dessus: à un moment donné de la hiérarchie, une interface a des invariants contradictoires. L'un dit "cet ensemble doit être modifiable" et l'autre dit "cet ensemble ne peut pas changer".Il pourrait y avoir deux hiérarchies complètement distinctes pour les structures de données mutables et immuables. Cela ajoute une tonne de complexité supplémentaire pour ce qui finit par être très peu de gain. Cela a également la faiblesse spécifique des méthodes qui ne se soucient pas de la mutabilité (par exemple, je veux juste parcourir une liste) doivent désormais prendre en charge deux interfaces distinctes. Étant donné que Java est de type statique, cela signifie des méthodes supplémentaires pour gérer les deux hiérarchies d'interface.
Nous pourrions avoir une interface unique et permettre aux implémentations de lever des exceptions si une méthode ne lui est pas applicable. C'est la route que Java a empruntée, et elle est la plus logique. Le nombre d'interfaces est réduit au minimum, et il n'y a pas d'invariants de mutabilité car l'interface documentée ne garantit en aucun cas la mutabilité . Si un invariant d'immuabilité est requis, utilisez les wrappers de
Collections
. Si une méthode n'a pas besoin de modifier une collection, ne la modifiez tout simplement pas. L'inconvénient est qu'une méthode ne peut pas garantir qu'une collection ne changera pas dans un autre thread si elle est fournie une collection de l'extérieur, mais c'est une préoccupation de la méthode appelante (ou de sa méthode d'appel) de toute façon.Lecture connexe: Pourquoi Java 8 n'inclut-il pas de collections immuables?
la source
MutableCollection
?C'est essentiellement YAGNI. Toutes les collections concrètes de la bibliothèque standard sont modifiables, implémentant ou héritant des opérations facultatives. Ils ne se soucient pas des collections immuables à usage général, pas plus que la grande majorité des développeurs Java. Ils ne créeront pas une hiérarchie d'interface complète uniquement pour les collections immuables, puis n'incluront aucune implémentation.
D'un autre côté, il existe quelques valeurs à usage spécial ou collections "virtuelles" qui pourraient être très utiles en tant qu'immuables, comme un ensemble vide et nCopies . En outre, il existe des collections immuables tierces (telles que Scala), qui pourraient vouloir appeler le code Java existant, de sorte qu'elles ont laissé la possibilité de collections immuables ouvertes de la manière la moins perturbatrice.
la source