
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 {
Ă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 {
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 {
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:
- Techniques corporelles longues, complexes et difficiles Ă comprendre.
- 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