Histoire de l'Ă©volution des interfaces en Java

image

L'interface en Java a beaucoup évolué au fil des ans. Voyons quels changements se sont produits au cours de son développement.

Interfaces originales


Les interfaces de Java 1.0 étaient assez simples par rapport à ce qu'elles sont maintenant. Ils ne peuvent contenir que deux types d'éléments: les constantes et les méthodes abstraites publiques.

Champs constants


Les interfaces peuvent contenir des champs, tout comme les classes ordinaires, mais avec quelques différences:

  • Les champs doivent ĂȘtre initialisĂ©s.
  • Les champs sont considĂ©rĂ©s comme une finale publique statique
  • Les modificateurs public, static et final n'ont pas besoin d'ĂȘtre spĂ©cifiĂ©s explicitement (ils sont dĂ©posĂ©s par dĂ©faut)

public interface MyInterface { int MY_CONSTANT = 9; } 

MĂȘme si cela n'est pas spĂ©cifiĂ© explicitement, le champ MY_CONSTANT est considĂ©rĂ© comme une constante finale statique publique. Vous pouvez ajouter ces modificateurs, mais ce n'est pas nĂ©cessaire.

MĂ©thodes abstraites


Les éléments les plus importants d'une interface sont ses méthodes. Les méthodes d'interface diffÚrent également des méthodes de classe normales:

  • Les mĂ©thodes n'ont pas de corps
  • L'implĂ©mentation de la mĂ©thode est fournie par les classes qui implĂ©mentent cette interface.
  • Les mĂ©thodes sont considĂ©rĂ©es comme publiques et abstraites mĂȘme si elles ne sont pas explicitement spĂ©cifiĂ©es.
  • Les mĂ©thodes ne peuvent pas ĂȘtre finales, car la combinaison de modificateurs abstraits et finaux n'est pas autorisĂ©e en Java

 public interface MyInterface { int doSomething(); String doSomethingCompletelyDifferent(); } 

Imbrication


Java 1.1 a introduit le concept de classes qui peuvent ĂȘtre placĂ©es dans d'autres classes. Ces classes sont de deux types: statiques et non statiques. Les interfaces peuvent Ă©galement contenir d'autres interfaces et classes.

MĂȘme si cela n'est pas spĂ©cifiĂ© explicitement, ces interfaces et classes sont considĂ©rĂ©es comme publiques et statiques.

 public interface MyInterface { class MyClass { //... } interface MyOtherInterface { //... } } 

ÉnumĂ©rations et annotations


Deux autres types ont Ă©tĂ© introduits dans Java 5: les Ă©numĂ©rations et les annotations. Ils peuvent Ă©galement ĂȘtre placĂ©s Ă  l'intĂ©rieur des interfaces.

 public interface MyInterface { enum MyEnum { FOO, BAR; } @interface MyAnnotation { //... } } 

Types génériques


Java 5 a introduit le concept des génériques, des types génériques. En bref: les génériques vous permettent d'utiliser un type générique au lieu de spécifier un type spécifique. Ainsi, vous pouvez écrire du code qui fonctionne avec un nombre différent de types sans sacrifier la sécurité et sans fournir une implémentation distincte pour chaque type.

Dans les interfaces commençant par Java 5, vous pouvez définir un type générique, puis l'utiliser comme type de valeur de retour d'une méthode ou comme type d'argument d'une méthode.

L'interface Box fonctionne que vous l'utilisiez pour stocker des objets tels que String, Integer, List, Shoe ou tout autre.

 interface Box<T> { void insert(T item); } class ShoeBox implements Box<Shoe> { public void insert(Shoe item) { //... } } 

MĂ©thodes statiques


À partir de Java 8, vous pouvez inclure des mĂ©thodes statiques dans les interfaces. Cette approche a changĂ© le fonctionnement de l'interface pour nous. Ils fonctionnent maintenant trĂšs diffĂ©remment de la façon dont ils fonctionnaient avant Java 8. Initialement, toutes les mĂ©thodes des interfaces Ă©taient abstraites. Cela signifiait que l'interface ne fournissait qu'une signature, mais pas une implĂ©mentation. L'implĂ©mentation a Ă©tĂ© laissĂ©e aux classes implĂ©mentant votre interface.

Lorsque vous utilisez des méthodes statiques dans les interfaces, vous devez également fournir une implémentation du corps de la méthode. Pour utiliser cette méthode dans une interface, utilisez simplement le mot-clé statique. Les méthodes statiques sont considérées comme publiques par défaut.

 public interface MyInterface { // This works static int foo() { return 0; } // This does not work, // static methods in interfaces need body static int bar(); } 

Héritage de méthode statique


Contrairement aux méthodes statiques classiques, les méthodes statiques dans les interfaces ne sont pas héritées. Cela signifie que si vous souhaitez appeler une telle méthode, vous devez l'appeler directement à partir de l'interface, et non à partir de la classe qui l'implémente.

 MyInterface.staticMethod(); 

Ce comportement est trĂšs utile pour Ă©viter plusieurs problĂšmes d'hĂ©ritage. Imaginez que vous ayez une classe qui implĂ©mente deux interfaces. Chacune des interfaces a une mĂ©thode statique avec le mĂȘme nom et la mĂȘme signature. Lequel doit ĂȘtre utilisĂ© en premier?

Pourquoi est-ce utile


Imaginez que vous ayez une interface et un ensemble complet de méthodes d'assistance qui fonctionnent avec les classes qui implémentent cette interface.

Traditionnellement, il y a eu une approche pour utiliser une classe d'accompagnement. En plus de l'interface, une classe utilitaire a été créée avec un nom trÚs similaire contenant des méthodes statiques appartenant à l'interface.

Vous pouvez trouver des exemples d'utilisation de cette approche directement dans le JDK: l'interface java.util.Collection et la classe utilitaire java.util.Collections qui l'accompagnent.

Avec des méthodes statiques dans les interfaces, cette approche n'est plus pertinente, n'est pas nécessaire et n'est pas recommandée. Maintenant, vous pouvez tout avoir en un seul endroit.

Méthodes par défaut


Les mĂ©thodes par dĂ©faut sont similaires aux mĂ©thodes statiques dans la mesure oĂč vous devez Ă©galement leur fournir un corps. Pour dĂ©clarer une mĂ©thode par dĂ©faut, utilisez simplement le mot clĂ© par dĂ©faut.

 public interface MyInterface { default int doSomething() { return 0; } } 

Contrairement aux méthodes statiques, les méthodes sont héritées par défaut des classes qui implémentent l'interface. Ce qui est important, ces classes peuvent redéfinir leur comportement si nécessaire.

Bien qu'il y ait une exception. L'interface ne peut pas avoir de mĂ©thodes par dĂ©faut avec la mĂȘme signature que les mĂ©thodes toString, equals et hashCode de la classe Object. Jetez un Ɠil Ă  la rĂ©ponse de Brian Goetz pour comprendre la validitĂ© d'une telle solution: Autorisez les mĂ©thodes par dĂ©faut Ă  remplacer les mĂ©thodes d'Object.

Pourquoi est-ce utile


L'idée d'implémenter des méthodes directement dans l'interface ne semble pas tout à fait correcte. Alors pourquoi cette fonctionnalité a-t-elle été introduite pour la premiÚre fois?

Les interfaces ont un problĂšme. DĂšs que vous donnez votre API Ă  d'autres personnes, elle «pĂ©trifiera» pour toujours (elle ne peut pas ĂȘtre modifiĂ©e sans douleur).

Par tradition, Java prend la compatibilitĂ© descendante trĂšs au sĂ©rieux. Les mĂ©thodes par dĂ©faut permettent d'Ă©tendre les interfaces existantes avec de nouvelles mĂ©thodes. Plus important encore, les mĂ©thodes par dĂ©faut fournissent dĂ©jĂ  une implĂ©mentation spĂ©cifique. Cela signifie que les classes implĂ©mentant votre interface n'ont pas besoin d'implĂ©menter de nouvelles mĂ©thodes. Mais, si nĂ©cessaire, les mĂ©thodes par dĂ©faut peuvent ĂȘtre remplacĂ©es Ă  tout moment si leur implĂ©mentation cesse de convenir. Donc, en bref, vous pouvez fournir de nouvelles fonctionnalitĂ©s aux classes existantes qui implĂ©mentent votre interface, tout en conservant la compatibilitĂ©.

Conflits


Imaginons que nous ayons une classe qui implĂ©mente deux interfaces. Ces interfaces ont une mĂ©thode par dĂ©faut avec le mĂȘme nom et la mĂȘme signature.

 interface A { default int doSomething() { return 0; } } interface B { default int doSomething() { return 42; } } class MyClass implements A, B { } 

DĂ©sormais, la mĂȘme mĂ©thode par dĂ©faut avec la mĂȘme signature est hĂ©ritĂ©e de deux interfaces diffĂ©rentes. Chaque interface a sa propre implĂ©mentation de cette mĂ©thode.

Alors, comment notre classe sait-elle laquelle des deux implémentations différentes utiliser?

Il ne le saura pas. Le code ci-dessus entraßnera une erreur de compilation. Si vous devez le faire fonctionner, vous devez remplacer la méthode conflictuelle dans votre classe.

 interface A { default int doSomething() { return 0; } } interface B { default int doSomething() { return 42; } } class MyClass implements A, B { // Without this the compilation fails @Override public int doSomething() { return 256; } } 

Méthodes privées


Avec l'avÚnement de Java 8 et l'introduction des méthodes par défaut et des méthodes statiques, les interfaces ont désormais la capacité de contenir non seulement les signatures de méthode, mais aussi leur implémentation. Lors de l'écriture de telles implémentations, il est recommandé de diviser les méthodes complexes en méthodes plus simples. Ce code est plus facile à réutiliser, à maintenir et à comprendre.

À cette fin, vous utiliseriez des mĂ©thodes privĂ©es, car elles peuvent contenir tous les dĂ©tails d'implĂ©mentation qui ne devraient pas ĂȘtre visibles et utilisĂ©s de l'extĂ©rieur.

Malheureusement en Java 8, une interface ne peut pas contenir de méthodes privées. Cela signifie que vous pouvez utiliser:

  1. Techniques corporelles longues, complexes et difficiles Ă  comprendre.
  2. Méthodes d'assistance faisant partie de l'interface. Cela viole le principe d'encapsulation et pollue l'API publique de l'interface et des classes d'implémentation.

Heureusement, à partir de Java 9, vous pouvez utiliser des méthodes privées dans les interfaces . Ils ont les caractéristiques suivantes:

  • les mĂ©thodes privĂ©es ont un corps, elles ne sont pas abstraites
  • ils peuvent ĂȘtre statiques ou non statiques
  • ils ne sont pas hĂ©ritĂ©s par les classes qui implĂ©mentent l'interface et les interfaces
  • ils peuvent appeler d'autres mĂ©thodes d'interface
  • les mĂ©thodes privĂ©es peuvent appeler d'autres mĂ©thodes privĂ©es, abstraites, statiques ou par dĂ©faut
  • les mĂ©thodes statiques privĂ©es ne peuvent appeler que d'autres mĂ©thodes statiques statiques et privĂ©es

 public interface MyInterface { private static int staticMethod() { return 42; } private int nonStaticMethod() { return 0; } } 

Ordre chronologique


Voici une liste chronologique des modifications par version de Java:

Java 1.1


Classes imbriquées

Interfaces imbriquées

Java 5


Types génériques

Transferts fermés

Annotations imbriquées

Java 8


Méthodes par défaut

MĂ©thodes statiques

Java 9


Méthodes privées

Source: https://habr.com/ru/post/fr482498/


All Articles