Comme Java 8 permet l’implémentation par défaut des méthodes dans l’interface appelée Méthodes par défaut , il semble exister une confusion entre quand utiliserais-je un abstract class
.
Alors, quand faut-il utiliser l'interface avec les méthodes par défaut et quand utiliser une classe abstraite? Les classes abstraites sont-elles toujours utiles dans ce scénario?
Les classes abstraites représentent bien plus que des implémentations de méthodes par défaut (telles que private state), mais à partir de Java 8, chaque fois que vous avez le choix, vous devez utiliser la méthode defender (ou. default
) dans l'interface.
La contrainte sur la méthode par défaut est qu'elle ne peut être implémentée qu'en termes d'appels à d'autres méthodes d'interface, sans référence à l'état d'une implémentation particulière. Le principal cas d'utilisation est donc les méthodes de niveau supérieur et de commodité.
La bonne chose à propos de cette nouvelle fonctionnalité est que, là où auparavant vous étiez obligé d'utiliser une classe abstraite pour les méthodes pratiques, contraignant ainsi l'implémenteur à un héritage unique, vous pouvez maintenant avoir une conception vraiment épurée avec juste l'interface et un minimum d'implémentation effort forcé sur le programmeur.
La motivation initiale pour introduire des méthodes default
à Java 8 était le désir d'étendre les interfaces de Framework Framework avec des méthodes orientées lambda sans rompre aucune implémentation existante. Bien que cela soit plus pertinent pour les auteurs de bibliothèques publiques, vous pouvez également trouver la même fonctionnalité utile dans votre projet. Vous disposez d'un emplacement centralisé pour ajouter de nouvelles fonctionnalités et vous n'avez pas à vous fier à l'apparence du reste de la hiérarchie des types.
Il y a quelques différences techniques. Les classes abstraites peuvent encore faire plus par rapport aux interfaces Java 8:
Sur le plan conceptuel, l'objectif principal des méthodes defender est la compatibilité avec les versions antérieures après l'introduction de nouvelles fonctionnalités (comme les fonctions lambda) dans Java 8.
Ceci est décrit dans cet article article . Pensez à forEach
de collections.
List<?> list = …
list.forEach(…);
Le forEach n'est pas déclaré par
Java.util.List
ni leJava.util.Collection
interface pour le moment. Une solution évidente serait de ajoutez simplement la nouvelle méthode à l'interface existante et fournissez le fichier mise en œuvre si nécessaire dans le JDK. Cependant, une fois publié, il est impossible d'ajouter des méthodes à une interface sans casser le mise en œuvre existante.L’avantage des méthodes par défaut est qu’il est maintenant possible de ajoutez une nouvelle méthode par défaut à l’interface et cela ne cassera pas le .__ mises en œuvre.
Comme décrit dans this article,
Classes abstraites versus interfaces en Java 8
Après avoir introduit Default Method, il semble que les interfaces et les classes abstraites sont les mêmes. Cependant, ils sont toujours un concept différent en Java 8.
La classe abstraite peut définir un constructeur. Ils sont plus structurés et peut avoir un état qui leur est associé. En revanche, par défaut Cette méthode ne peut être implémentée qu’en invoquant une autre méthodes d'interface, sans référence à une implémentation particulière Etat. Par conséquent, les deux utilisent à des fins différentes et en choisissant entre deux dépend vraiment du contexte du scénario.
Ces deux sont très différents:
Les méthodes par défaut consistent à ajouter une fonctionnalité externe aux classes existantes sans modifier leur état.
Et les classes abstraites sont un type d'héritage normal, ce sont des classes normales qui doivent être étendues.
Concernant votre question de
Alors, quand faut-il utiliser l'interface avec les méthodes par défaut et quand utiliser une classe abstraite? Les classes abstraites sont-elles toujours utiles dans ce scénario?
Java documentation fournit une réponse parfaite.
Classes abstraites comparées aux interfaces:
Les classes abstraites sont similaires aux interfaces. Vous ne pouvez pas les instancier, et elles peuvent contenir un mélange de méthodes déclarées avec ou sans implémentation.
Toutefois, avec les classes abstraites, vous pouvez déclarer des champs non statiques et finaux et définir des méthodes concrètes publiques, protégées et privées.
Avec les interfaces, tous les champs sont automatiquement publics, statiques et finaux, et toutes les méthodes que vous déclarez ou définissez (en tant que méthodes par défaut) sont publiques. De plus, vous ne pouvez étendre qu'une seule classe, qu'elle soit abstraite ou non, alors que vous pouvez implémenter un nombre illimité d'interfaces.
Les cas d'utilisation de chacun d'entre eux ont été expliqués ci-dessous, poste SE:
Quelle est la différence entre une classe d'interface et abstraite?
Les classes abstraites sont-elles toujours utiles dans ce scénario?
Oui. Ils sont toujours utiles. Ils peuvent contenir des méthodes non statiques non finales et des attributs (protégés, privés en plus des publics), ce qui n'est pas possible même avec les interfaces Java-8.
Chaque fois que nous avons le choix entre une classe abstraite et une interface, nous devrions toujours (presque) préférer les méthodes par défaut (également appelées extensions de défenseur ou virtuelles).
Collection and AbstractCollection
. Nous devons maintenant implémenter les méthodes dans l'interface elle-même pour fournir une fonctionnalité par défaut. Les classes qui implémentent l'interface ont le choix de remplacer les méthodes ou d'hériter de l'implémentation par défaut.Une autre utilisation importante des méthodes par défaut est interface evolution
. Supposons que j'ai un ballon de classe comme:
public class Ball implements Collection { ... }
Maintenant, dans Java 8, une nouvelle fonctionnalité est introduite. Nous pouvons obtenir un flux en utilisant la méthode stream
ajoutée à l'interface. Si stream
n'était pas une méthode par défaut, toutes les implémentations de l'interface Collection
se seraient rompues car elles ne mettraient pas en œuvre cette nouvelle méthode. L'ajout d'une méthode autre que celle par défaut à une interface n'est pas source-compatible
.
Mais supposons que nous ne recompilons pas la classe et utilisons un ancien fichier jar contenant cette classe Ball
. La classe se chargera bien sans cette méthode manquante, des instances peuvent être créées et il semble que tout fonctionne correctement.MAISsi le programme appelle la méthode stream
sur une instance de Ball
, nous obtiendrons AbstractMethodError
. La méthode par défaut a donc résolu les deux problèmes.
Les méthodes par défaut dans l'interface Java permettent interface evolution.
Si vous souhaitez ajouter une méthode à une interface existante sans rompre la compatibilité binaire avec les anciennes versions de l'interface, vous avez deux options: ajouter une méthode par défaut ou statique. En effet, toute méthode abstraite ajoutée à l'interface devrait être implémentée par les classes ou les interfaces implémentant cette interface.
Une méthode statique est unique à une classe. Une méthode par défaut est unique à une instance de la classe.
Si vous ajoutez une méthode par défaut à une interface existante, les classes et les interfaces implémentant cette interface n'ont pas besoin de l'implémenter. Ils peuvent
Plus sur le sujet ici .
quand faut-il utiliser les méthodes par défaut et quand un classe abstraite être utilisé?
Compatibilité ascendante: Imaginez que votre interface soit implémentée par des centaines de classes, sa modification obligera tous les utilisateurs à implémenter la méthode nouvellement ajoutée, même si cela n’est pas indispensable pour de nombreuses autres interface, plus il permet à votre interface d'être une interface fonctionnelle
Faits et restrictions:
1-Peut uniquement être déclaré dans une interface et non dans une classe ou une classe abstraite
2-Doit fournir un corps
3-Il n'est pas supposé être abstrait comme les autres méthodes normales utilisées dans une interface.
En Java 8, une interface ressemble à une classe abstraite, bien qu'il puisse y avoir quelques différences, telles que:
1) Les classes abstraites sont des classes, elles ne sont donc pas limitées à d'autres restrictions de l'interface en Java, par exemple. La classe abstraite peut avoir l'état , mais vous ne pouvez pas avoir l'état sur l'interface en Java.
2) Une autre différence sémantique entre l'interface avec les méthodes par défaut et la classe abstraite est que vous pouvez définir des constructeurs à l'intérieur d'une classe abstraite , mais vous ne pouvez pas définir un constructeur à l'intérieur de l'interface en Java.
Comme mentionné dans d'autres réponses, la possibilité d'ajouter une implémentation à une interface a été ajoutée afin d'assurer la compatibilité avec les versions antérieures du framework Collections. Je dirais que la compatibilité ascendante est potentiellement la seule bonne raison d’ajouter une implémentation à une interface.
Sinon, si vous ajoutez l'implémentation à une interface, vous enfreignez la loi fondamentale pour laquelle les interfaces ont été ajoutées en premier lieu. Java est un langage à héritage unique, contrairement au C++ qui permet pour héritage multiple. Les interfaces offrent les avantages de typage fournis avec un langage prenant en charge l'héritage multiple sans introduire les problèmes liés à l'héritage multiple.
Plus spécifiquement, Java n'autorise l'héritage qu'une implémentation, mais autorise l'héritage de plusieurs interfaces. Par exemple, ce qui suit est un code Java valide:
class MyObject extends String implements Runnable, Comparable { ... }
MyObject
hérite d'une seule implémentation, mais de trois contrats.
Java a transmis plusieurs héritages d'implémentation, car plusieurs héritages d'implémentation sont livrés avec une foule de problèmes épineux, qui sortent du cadre de cette réponse. Des interfaces ont été ajoutées pour permettre l'héritage multiple de contrats (également appelé interfaces) sans les problèmes d'héritage multiple d'implémentation.
Pour appuyer mon propos, voici une citation de Ken Arnold et James Gosling tirée du livre . Le Java Langage de programmation, 4ème édition :
L'héritage unique exclut certaines conceptions utiles et correctes. Les problèmes d'héritage multiple proviennent d'un héritage multiple d'implémentation, mais dans de nombreux cas, l'héritage multiple est utilisé pour hériter d'un certain nombre de contrats abstraits et peut-être d'une implémentation concrète. Fournir un moyen d'hériter d'un contrat abstrait sans hériter d'une implémentation permet de profiter des avantages de typage d'un héritage multiple sans les problèmes d'héritage de plusieurs implémentations. L'héritage d'un contrat abstrait est appelé héritage d'interface . Le langage de programmation Java prend en charge l'héritage d'interface en vous permettant de déclarer un type
interface
.
S'il vous plaît pensez d'abord au principe d'ouverture/fermeture. Les méthodes par défaut des interfaces le VIOLATENT. C'est une mauvaise fonctionnalité en Java. Cela encourage une mauvaise conception, une mauvaise architecture et une qualité logicielle médiocre. Je suggère d'éviter d'utiliser complètement les méthodes par défaut.
Posez-vous quelques questions: Pourquoi ne pouvez-vous pas mettre vos méthodes en classe abstraite? Auriez-vous besoin de plus d'une classe abstraite? Ensuite, réfléchissez à ce que votre classe est responsable de. Etes-vous sûr que toutes les méthodes que vous allez mettre dans la même classe remplissent vraiment le même objectif? Peut-être distinguerez-vous plusieurs objectifs et diviserez ensuite votre classe en plusieurs classes, chacune ayant sa propre classe.
Les méthodes par défaut dans l'interface Java doivent être davantage utilisées pour fournir une implémentation fictive d'une fonction, ce qui évite à toute classe d'implémentation de cette interface de déclarer toutes les méthodes abstraites, même si elles ne veulent traiter qu'une seule .. interface sont donc en quelque sorte un remplacement du concept de classe d’adaptateur.
Les méthodes de la classe abstraite sont toutefois supposées donner une implémentation significative que toute classe enfant ne doit remplacer que si cela est nécessaire pour remplacer une fonctionnalité commune.