La méthode par défaut ajoute une très bonne nouvelle fonctionnalité au jeu d'instructions de JVM. Après avoir utilisé la méthode par défaut, si une nouvelle méthode est ajoutée à l'interface de la bibliothèque, la classe utilisateur qui implémente cette interface peut automatiquement obtenir l'implémentation par défaut de cette méthode. Une fois que l'utilisateur souhaite mettre à jour sa classe d'implémentation, il suffit de remplacer cette méthode par défaut et de la remplacer par une implémentation qui rend plus significative dans un scénario spécifique. Ce qui est encore mieux, c'est que les utilisateurs peuvent appeler l'implémentation par défaut de l'interface dans la méthode réécrite pour ajouter des fonctions supplémentaires.
Tout est assez bon jusqu'à présent. Cependant, l'ajout de méthodes par défaut aux interfaces Java existantes peut entraîner une incompatibilité du code . C'est facile à comprendre en regardant un exemple. Supposons qu'il existe une bibliothèque qui oblige l'utilisateur à implémenter l'une de ses interfaces en entrée:
interface SimpleInput {void foo (); void bar ();} classe abstraite SimpleInputAdapter implémente SimpleInput {@Override public void bar () {// un comportement par défaut ...}}Avant Java 8, la combinaison de l'interface ci-dessus et d'une classe d'adaptateur correspondante était un modèle très courant dans la langue java. Les développeurs de bibliothèques de classe fournissent un adaptateur pour réduire la quantité de codage pour les consommateurs de bibliothèque. Cependant, le but de fournir cette interface est en fait d'atteindre une relation similaire à l'héritage multiple.
Supposons qu'un utilisateur utilise cet adaptateur:
class MyInput étend SimpleInputAdapter {@Override public void foo () {// faire quelque chose ...} @Override public void bar () {super.bar (); // faire quelque chose en plus ...}}Avec cette implémentation, les utilisateurs peuvent interagir avec la bibliothèque. Remarquez comment cette implémentation remplace la méthode de la barre pour ajouter des fonctionnalités supplémentaires à l'implémentation par défaut.
Alors, que se passerait-il si cette bibliothèque était migrée vers Java 8? Tout d'abord, cette bibliothèque est susceptible de jeter la classe d'adaptateur et de migrer cette fonction vers la méthode par défaut. En fin de compte, cette interface ressemblera à ceci:
interface SimpleInput {void foo (); par défaut void bar () {// un comportement par défaut}}}Avec cette nouvelle interface, l'utilisateur doit mettre à jour son code pour utiliser cette méthode par défaut au lieu de la classe d'adaptateur. L'un des grands avantages de l'utilisation d'une nouvelle interface au lieu d'une classe d'adaptateur est que les utilisateurs peuvent hériter d'une autre classe au lieu de cette classe d'adaptateur. Commençons par pratiquer et transformer la classe MyInput en utilisant la méthode par défaut. Étant donné que nous pouvons maintenant hériter d'autres classes, nous essaierons d'étendre une classe de base tierce supplémentaire. Ce que fait cette classe de base n'est pas important ici. Supposons que cela a du sens pour notre cas d'utilisation.
class MyInput étend ThirdPartyBaseClass implémente SimpleInput {@Override public void foo () {// faire quelque chose ...} @Override public void bar () {SimpleInput.super.foo (); // faire quelque chose en plus ...}}Afin d'atteindre la même fonction que la classe d'origine, nous avons utilisé la nouvelle syntaxe Java 8 pour appeler les méthodes par défaut de l'interface. De même, nous avons mis la logique de MyMethod dans une classe de base MyBase. Vous pouvez vous détendre après vous battre les épaules. C'était génial après le refactorisation!
Cette bibliothèque que nous utilisons a été considérablement améliorée. Cependant, le personnel de maintenance doit ajouter une autre interface pour implémenter certaines fonctionnalités supplémentaires. Cette interface est appelée Compexinput, qui hérite de la classe SimpleInput et ajoute une méthode supplémentaire. Comme on pense généralement que la méthode par défaut peut être ajoutée en toute confiance, le personnel de maintenance a remplacé la méthode par défaut de la classe SimpleInput et a ajouté des actions supplémentaires pour fournir à l'utilisateur une meilleure implémentation par défaut. Après tout, cela est très courant lors de l'utilisation de classes d'adaptateur:
Interface complexinput étend SimpleInput {void Qux (); @Override default void bar () {SimpleInput.super.bar (); // Si complexe, nous devons faire plus ...}}Cette nouvelle fonctionnalité semble très bonne, donc le mainteneur de la classe ThirdPartyBasEClass a également décidé d'utiliser cette bibliothèque. Pour y parvenir, il a mis en œuvre la classe ThirdPartyBaseClass à l'interface complexinput.
Mais qu'est-ce que cela signifie pour la classe MyInput? Puisqu'il hérite de la classe ThirdPartyBaseClass, l'interface complexinput est implémentée par défaut. De cette façon, appeler la méthode par défaut de SimpleInput n'est pas légal. Le résultat est que le code de l'utilisateur ne peut pas être compilé à la fin. De plus, cette méthode est complètement impossible à appeler, car Java considère cette méthode super-super pour appeler la classe parent indirecte illégale. Vous ne pouvez appeler que la méthode par défaut de l'interface complexinput. Cependant, cela vous oblige d'abord à implémenter explicitement cette interface dans la classe MyInput. Pour les utilisateurs de cette bibliothèque, ces modifications sont complètement inattendues.
(Remarque: pour le dire simplement, c'est en fait:
L'interface a {par défaut void test () {}} L'interface B étend un test {par défaut void () {}} Le test de classe publique implémente b {public void test () {b.super.test (); //A.super.test (); Erreur }}Bien sûr, si vous écrivez ceci, l'utilisateur a activement choisi d'implémenter l'interface B. L'exemple de l'article a introduit une classe de base, donc un changement apparemment non influentiel a été apporté dans la bibliothèque et la classe de base, mais en fait, cela a provoqué la compilation du code utilisateur)
Étrangement, Java ne distingue pas cela lors de l'exécution. Le validateur de JVM permet à une classe compilée d'appeler la méthode SimpleInput :: FOO, bien que la classe chargée hérite de la version mise à jour de ThirdPartyBaseClass et implémente implicitement l'interface complexinput. Si vous souhaitez blâmer, vous ne pouvez blâmer le compilateur. (Remarque: le compilateur et le comportement d'exécution sont incohérents)
Alors qu'en avons-nous appris? Autrement dit, ne remplacez pas la méthode par défaut de l'interface d'origine dans une autre interface. Ne le réécrivez pas avec une autre méthode par défaut et ne le réécrivez pas avec une méthode abstraite. En bref, vous devez être très prudent lorsque vous utilisez la méthode par défaut. Bien qu'ils facilitent l'amélioration des interfaces des bibliothèques de collecte existantes de Java, il vous permet de faire des appels de méthode dans la structure de succession de la classe, ce qui augmente essentiellement la complexité. Avant Java 7, il vous suffit de traverser la hiérarchie de classe linéaire et de regarder le code d'appel réel. Lorsque vous pensez que vous en avez vraiment besoin, utilisez la méthode par défaut.
Ce qui précède est une explication détaillée de la raison pour laquelle vous devez utiliser la méthode par défaut de Java 8 avec prudence. J'espère que cela sera utile à l'apprentissage de tous.