Dans le Chapitre 6, vous avez appris sur les classes abstraites, particulièrement comment en créer et en étendre une. Puisque les classes ne peuvent étendre qu’une seule classe, elles ont une utilisation limitée pour l’héritage. D’autre part, une classe peut implémenter n’importe quel nombre d’interfaces. Une interface est un type de données abstrait qui déclare une liste de méthodes abstraites que toute classe implémentant l’interface doit fournir.
Au fil du temps, la définition précise d’une interface a changé, car de nouveaux types de méthodes sont maintenant pris en charge. Dans ce chapitre, nous commençons par une définition rudimentaire d’une interface et l’étendons pour couvrir tous les membres pris en charge.
Déclaration et Utilisation d’une Interface
En Java, une interface est définie avec le mot-clé interface
, analogue au mot-clé class
utilisé lors de la définition d’une classe. Référez-vous à la Figure 7.1 pour une déclaration d’interface appropriée.
L’illustration originale montre la syntaxe détaillée d’une déclaration d’interface avec l’exemple suivant:
public abstract interface PeutCreuser {
public abstract Float getVitesse(int age);
public static final int PROFONDEUR_MINIMUM = 2;
}
Cette figure illustre les modificateurs implicites et la structure d’une interface.
Dans une déclaration d’interface, nous incluons généralement des méthodes abstraites et des variables constantes. Les variables d’interface sont appelées constantes car elles sont supposées être public
, static
, et final
. Elles sont initialisées avec une valeur constante lorsqu’elles sont déclarées. Puisqu’elles sont public
et static
, elles peuvent être utilisées en dehors de la déclaration d’interface sans nécessiter une instance de l’interface. Une interface inclut également des méthodes abstraites qui, comme les variables d’interface, sont supposées être public
.
Pour être bref, nous disons souvent “une instance d’une interface” dans ce chapitre pour signifier une instance d’une classe qui implémente l’interface.
Que signifie pour une variable ou une méthode d’être supposée être quelque chose? Un aspect d’une déclaration d’interface qui diffère d’une classe abstraite est qu’elle contient des modificateurs implicites. Un modificateur implicite est un modificateur que le compilateur insère automatiquement dans le code. Par exemple, une interface est toujours considérée comme étant abstract
, même si elle n’est pas marquée comme telle. Nous couvrons les règles et exemples pour les modificateurs implicites plus en détail sous peu.
Commençons par un exemple simple. Imaginons que nous avons une interface MarcheSurDeuxJambes
, définie comme suit:
public abstract interface MarcheSurDeuxJambes {}
Elle compile car les interfaces ne sont pas obligées de définir des méthodes. Le modificateur abstract
dans cet exemple est optionnel pour les interfaces, le compilateur l’insérant s’il n’est pas fourni. Maintenant, considérez les deux exemples suivants, qui ne compilent pas:
public class Bipede {
public static void main(String[] args) {
var e = new MarcheSurDeuxJambes(); // NE COMPILE PAS
}
}
public final interface MarcheSurHuitPattes {} // NE COMPILE PAS
Le premier exemple ne compile pas, car MarcheSurDeuxJambes
est une interface et ne peut pas être instanciée. Le deuxième exemple, MarcheSurHuitPattes
, ne compile pas car les interfaces ne peuvent pas être marquées comme final
pour la même raison que les classes abstraites ne peuvent pas être marquées comme final
. En d’autres termes, marquer une interface final
implique qu’aucune classe ne pourrait jamais l’implémenter.
Comment utilisez-vous une interface? Disons que nous avons une interface Grimper
, définie comme suit:
public interface Grimper {
Number getVitesse(int age);
}
Ensuite, nous avons une classe concrète SourisDeLaChamp
qui invoque l’interface Grimper
en utilisant le mot-clé implements
dans sa déclaration de classe.
L’illustration originale montre une classe implémentant une interface avec l’exemple suivant:
public class SourisDeLaChamp implements Grimper, PeutCreuser {
public Float getVitesse(int age) {
return 11f;
}
}
Cette figure illustre la syntaxe d’implémentation d’interfaces multiples et la surcharge de méthodes d’interface.
La classe SourisDeLaChamp
déclare qu’elle implémente l’interface Grimper
et inclut une version surchargée de getVitesse()
héritée de l’interface Grimper
. La signature de la méthode getVitesse()
correspond exactement, et le type de retour est covariant, puisqu’un Float
peut être implicitement converti en un Number
. Le modificateur d’accès de la méthode d’interface est implicitement public
dans Grimper
, bien que la classe concrète SourisDeLaChamp
doive le déclarer explicitement.
Comme le montre la Figure 7.2, une classe peut implémenter plusieurs interfaces, chacune séparée par une virgule (,). Si l’une des interfaces définit des méthodes abstraites, alors la classe concrète est requise pour les surcharger. Dans ce cas, SourisDeLaChamp
implémente l’interface PeutCreuser
que nous avons vue dans la Figure 7.1. De cette manière, la classe surcharge deux méthodes abstraites en même temps avec une déclaration de méthode. Vous en apprendrez davantage sur les méthodes d’interface dupliquées et compatibles dans ce chapitre.
Extension d’une Interface
Comme une classe, une interface peut étendre une autre interface en utilisant le mot-clé extends
.
public interface Nocturne {}
public interface AGrosYeux extends Nocturne {}
Contrairement à une classe, qui ne peut étendre qu’une seule classe, une interface peut étendre plusieurs interfaces.
public interface Nocturne {
public int chasser();
}
public interface PeutVoler {
public void battreDesAiles();
}
public interface AGrosYeux extends Nocturne, PeutVoler {}
public class Hibou implements AGrosYeux {
public int chasser() { return 5; }
public void battreDesAiles() { System.out.println("Batt!"); }
}
Dans cet exemple, la classe Hibou
implémente l’interface AGrosYeux
et doit implémenter les méthodes chasser()
et battreDesAiles()
. L’extension de deux interfaces est permise car les interfaces ne sont pas initialisées dans le cadre d’une hiérarchie de classes. Contrairement aux classes abstraites, elles ne contiennent pas de constructeurs et ne font pas partie de l’initialisation d’instance. Les interfaces définissent simplement un ensemble de règles et de méthodes qu’une classe les implémentant doit suivre.
Héritage d’une Interface
Comme une classe abstraite, lorsqu’une classe concrète hérite d’une interface, toutes les méthodes abstraites héritées doivent être implémentées. Nous illustrons ce principe ci-dessous. Combien de méthodes abstraites la classe concrète Cygne
hérite-t-elle?
Note sur la Figure 7.3 (Héritage d’Interface): L’illustration originale montre un diagramme d’héritage avec:
- Une classe abstraite
Animal
avec une méthode abstraitegetType()
- Une classe abstraite
Oiseau
qui étendAnimal
et ajoute une méthode abstraitepeutPlonger()
- Une interface
Voler
avec une méthodevoler()
- Une interface
Nager
avec une méthodenager()
- La classe
Cygne
qui étendOiseau
et implémente les interfacesVoler
etNager
Vous abandonnez? La classe concrète Cygne
hérite de quatre méthodes abstraites qu’elle doit implémenter: getType()
, peutPlonger()
, voler()
, et nager()
. Voyons un autre exemple impliquant une classe abstraite qui implémente une interface:
public interface AUneQueue {
public int getLongueurQueue();
}
public interface AMoustaches {
public int getNombreMoustaches();
}
public abstract class PhoqueDePort implements AUneQueue, AMoustaches {}
public class PhoqueCommun extends PhoqueDePort {} // NE COMPILE PAS
La classe PhoqueDePort
compile car elle est abstract
et n’est pas obligée d’implémenter l’une des méthodes abstraites qu’elle hérite. La classe concrète PhoqueCommun
, cependant, doit surcharger toutes les méthodes abstraites héritées.
Mélange des Mots-clés de Classe et d’Interface
Soyez attentif aux exemples qui mélangent les déclarations de classe et d’interface.
Bien qu’une classe puisse implémenter une interface, une classe ne peut pas étendre une interface. De même, bien qu’une interface puisse étendre une autre interface, une interface ne peut pas implémenter une autre interface. Les exemples suivants illustrent ces principes:
public interface PeutCourir {}
public class Guepard extends PeutCourir {} // NE COMPILE PAS
public class Hyene {}
public interface AFourrure extends Hyene {} // NE COMPILE PAS
Le premier exemple montre une classe essayant d’étendre une interface et ne compile pas. Le deuxième exemple montre une interface essayant d’étendre une classe, ce qui ne compile pas non plus.
Héritage de Méthodes Abstraites Dupliquées
Java prend en charge l’héritage de deux méthodes abstraites qui ont des déclarations de méthode compatibles.
public interface Herbivore { public void mangerPlantes(); }
public interface Omnivore { public void mangerPlantes(); }
public class Ours implements Herbivore, Omnivore {
public void mangerPlantes() {
System.out.println("Manger des plantes");
}
}
Par compatibles, nous entendons qu’une méthode peut être écrite qui surcharge correctement les deux méthodes héritées: par exemple, en utilisant des types de retour covariants que vous avez appris dans le Chapitre 6.
Voici un exemple d’une déclaration incompatible:
public interface Herbivore { public void mangerPlantes(); }
public interface Omnivore { public int mangerPlantes(); }
public class Tigre implements Herbivore, Omnivore { // NE COMPILE PAS
…
}
Il est impossible d’écrire une version de Tigre
qui satisfait les deux méthodes abstraites héritées. Le code ne compile pas, quel que soit ce qui est déclaré à l’intérieur de la classe Tigre
.
Insertion de Modificateurs Implicites
Comme mentionné précédemment, un modificateur implicite est celui que le compilateur insérera automatiquement. C’est semblable au compilateur insérant un constructeur sans argument par défaut si vous n’en définissez pas, ce que vous avez appris dans le Chapitre 6. Vous pouvez choisir d’insérer ces modificateurs implicites vous-même ou laisser le compilateur les insérer pour vous.
La liste suivante comprend les modificateurs implicites pour les interfaces que vous devez connaître:
- Les interfaces sont implicitement
abstract
. - Les variables d’interface sont implicitement
public
,static
, etfinal
. - Les méthodes d’interface sans corps sont implicitement
abstract
. - Les méthodes d’interface sans le modificateur
private
sont implicitementpublic
.
La dernière règle s’applique aux méthodes abstract
, default
, et static
de l’interface, que nous couvrons dans la section suivante.
Jetons un coup d’œil à un exemple. Les deux définitions d’interface suivantes sont équivalentes, car le compilateur les convertira toutes les deux en la deuxième déclaration:
public interface Planer {
int HAUTEUR_MAX = 10;
boolean SOUS_EAU = true;
void voler(int vitesse);
abstract void decoller();
public abstract double plonger();
}
public abstract interface Planer {
public static final int HAUTEUR_MAX = 10;
public final static boolean SOUS_EAU = true;
public abstract void voler(int vitesse);
public abstract void decoller();
public abstract double plonger();
}
Dans cet exemple, nous avons marqué en gras les modificateurs implicites que le compilateur insère automatiquement. D’abord, le mot-clé abstract
est ajouté à la déclaration de l’interface. Ensuite, les mots-clés public
, static
, et final
sont ajoutés aux variables d’interface si elles n’existent pas déjà. Enfin, chaque méthode abstraite est précédée des mots-clés abstract
et public
si elle ne les contient pas déjà.
Modificateurs Conflictuels
Que se passe-t-il si un développeur marque une méthode ou une variable avec un modificateur qui entre en conflit avec un modificateur implicite? Par exemple, si une méthode abstraite est implicitement public
, peut-elle être explicitement marquée protected
ou private
?
public interface Danse {
private int compte = 4; // NE COMPILE PAS
protected void pas(); // NE COMPILE PAS
}
Aucune de ces déclarations de membre d’interface ne compile, car le compilateur appliquera le modificateur public
aux deux, résultant en un conflit.
Différences entre les Interfaces et les Classes Abstraites
Même si les classes abstraites et les interfaces sont toutes deux considérées comme des types abstraits, seules les interfaces utilisent des modificateurs implicites. En quoi les méthodes jouer()
diffèrent-elles dans les deux définitions suivantes?
abstract class Husky { // abstract requis dans la déclaration de classe
abstract void jouer(); // abstract requis dans la déclaration de méthode
}
interface Caniche { // abstract optionnel dans la déclaration d'interface
void jouer(); // abstract optionnel dans la déclaration de méthode
}
Ces deux définitions de méthode sont considérées comme abstraites. Cela dit, la classe Husky
ne compilera pas si la méthode jouer()
n’est pas marquée abstract
, alors que la méthode dans l’interface Caniche
compilera avec ou sans le modificateur abstract
.
Qu’en est-il du niveau d’accès de la méthode jouer()
? Pouvez-vous repérer quelque chose d’incorrect avec les définitions de classe suivantes qui utilisent nos types abstraits?
public class Webby extends Husky {
void jouer() {} // OK - jouer() est déclarée avec accès package dans Husky
}
public class Georgette implements Caniche {
void jouer() {} // NE COMPILE PAS - jouer() est public dans Caniche
}
La classe Webby
compile, mais la classe Georgette
ne compile pas. Bien que les deux implémentations de méthode soient identiques, la méthode dans la classe Georgette
réduit le modificateur d’accès sur la méthode de public
à l’accès package.
Déclaration de Méthodes Concrètes d’Interface
Alors que les interfaces ont commencé avec des méthodes abstraites et des constantes, elles ont grandi pour inclure beaucoup plus. Le Tableau 7.1 liste les six types de membres d’interface que vous devez connaître. Nous avons déjà couvert les méthodes abstraites et les constantes, donc nous nous concentrons sur les quatre méthodes concrètes restantes dans cette section.
TABLEAU 7.1 Types de membres d’interface
Type de membre | Type d’adhésion | Modificateurs requis | Modificateurs implicites | A une valeur ou un corps? |
---|---|---|---|---|
Variable constante | Classe | — | public static final | Oui |
Méthode abstract | Instance | — | public abstract | Non |
Méthode default | Instance | default | public | Oui |
Méthode static | Classe | static | public | Oui |
Méthode private | Instance | private | — | Oui |
Méthode private static | Classe | private static | — | Oui |
Dans le Tableau 7.1, le type d’adhésion détermine comment il est accessible. Une méthode avec un type d’adhésion de classe est partagée entre toutes les instances de l’interface, tandis qu’une méthode avec un type d’adhésion d’instance est associée à une instance particulière de l’interface.
À propos des Membres d’Interface protected ou Package
Aux côtés des méthodes public
, les interfaces prennent maintenant en charge les méthodes private
. Elles ne prennent pas en charge l’accès protected
, cependant, car une classe ne peut pas étendre une interface. Elles ne prennent pas non plus en charge l’accès package, bien que plus probablement pour des raisons de syntaxe et de compatibilité descendante. Puisque les méthodes d’interface sans modificateur d’accès ont été considérées comme implicitement public
, changer ce comportement en accès package briserait beaucoup de programmes existants!
Écriture d’une Méthode d’Interface default
Le premier type de méthode concrète avec lequel vous devriez vous familiariser est une méthode default
. Une méthode default
est une méthode définie dans une interface avec le mot-clé default
et inclut un corps de méthode. Elle peut être optionnellement surchargée par une classe implémentant l’interface.
Une utilisation des méthodes default
est pour la compatibilité descendante. Vous pouvez ajouter une nouvelle méthode default
à une interface sans avoir besoin de modifier toutes les classes existantes qui implémentent l’interface. Les classes plus anciennes utiliseront simplement l’implémentation par défaut de la méthode définie dans l’interface. C’est d’où vient le nom de méthode default
!
Voici un exemple d’une méthode default
définie dans une interface:
public interface EstSangFroid {
boolean aDesEcailles();
default double getTemperature() {
return 10.0;
}
}
Cet exemple définit deux méthodes d’interface, une abstraite et une default
. La classe Serpent
suivante, qui implémente EstSangFroid
, doit implémenter aDesEcailles()
mais peut s’appuyer sur l’implémentation par défaut de getTemperature()
ou surcharger la méthode avec sa propre version:
public class Serpent implements EstSangFroid {
public boolean aDesEcailles() { // Surcharge requise
return true;
}
public double getTemperature() { // Surcharge optionnelle
return 12;
}
}
Notez que le modificateur de méthode d’interface default
n’est pas le même que l’étiquette default
utilisée dans une instruction ou expression switch
. De même, bien que l’accès package soit parfois appelé accès par défaut, cette fonctionnalité est implémentée en omettant un modificateur d’accès. Désolé si c’est déroutant! Nous sommes d’accord que Java a surutilisé le mot default
au fil des ans!
Pour l’examen, vous devriez être familier avec diverses règles pour déclarer des méthodes default
.
Règles de Définition des Méthodes d’Interface Default
- Une méthode
default
ne peut être déclarée que dans une interface. - Une méthode
default
doit être marquée avec le mot-clédefault
et inclure un corps de méthode. - Une méthode
default
est implicitementpublic
. - Une méthode
default
ne peut pas être marquéeabstract
,final
, oustatic
. - Une méthode
default
peut être surchargée par une classe qui implémente l’interface. - Si une classe hérite de deux ou plusieurs méthodes
default
avec la même signature de méthode, alors la classe doit surcharger la méthode.
Ces règles devraient découler de ce que vous savez jusqu’à présent des classes, des interfaces et des méthodes static
. Par exemple, vous ne pouvez pas déclarer des méthodes static
sans corps dans les classes non plus. Comme les méthodes d’interface default
et abstract
, les méthodes d’interface static
sont implicitement public
si elles sont déclarées sans modificateur d’accès. Comme vous le verrez bientôt, vous pouvez utiliser le modificateur d’accès private
avec les méthodes static
.
Jetons un coup d’œil à une méthode d’interface static
:
public interface Sauter {
static int getHauteurSaut() {
return 8;
}
}
Puisque la méthode est définie sans modificateur d’accès, le compilateur insérera automatiquement le modificateur d’accès public
. La méthode getHauteurSaut()
fonctionne comme une méthode static
telle que définie dans une classe. En d’autres termes, elle peut être accédée sans une instance d’une classe.
public class Sauter {
public int sauter() {
return Sauter.getHauteurSaut();
}
}
La dernière règle sur l’héritage pourrait être un peu déroutante, alors regardons un exemple. Voici un exemple d’une classe Lapin
qui implémente Sauter
et ne compile pas:
public class Lapin implements Sauter {
public void afficherDetails() {
System.out.println(getHauteurSaut()); // NE COMPILE PAS
}
}
Sans une référence explicite au nom de l’interface, le code ne compilera pas, même si Lapin
implémente Sauter
. Cela peut être facilement corrigé en utilisant le nom de l’interface:
public class Lapin implements Sauter {
public void afficherDetails() {
System.out.println(Sauter.getHauteurSaut());
}
}
Remarquez que nous n’avons pas le même problème que lorsque nous avons hérité de deux méthodes d’interface default
avec la même signature. Java a “résolu” le problème de l’héritage multiple des méthodes d’interface static
en ne leur permettant pas d’être héritées!
Réutilisation de Code avec des Méthodes d’Interface private
Les deux derniers types de méthodes concrètes qui peuvent être ajoutées aux interfaces sont les méthodes d’interface private
et private static
. Comme les deux types de méthodes sont private
, elles ne peuvent être utilisées que dans la déclaration d’interface dans laquelle elles sont déclarées. Pour cette raison, elles ont été ajoutées principalement pour réduire la duplication de code. Par exemple, considérez l’exemple de code suivant:
public interface Programme {
default void reveil() { verifierHeure(7); }
private void petitDejeuner() { verifierHeure(9); }
static void faireExercice() { verifierHeure(18); }
private static void verifierHeure(int heure) {
if (heure > 17) {
System.out.println("Vous êtes en retard!");
} else {
System.out.println("Vous avez encore "+(17-heure)+" heures "
+ "pour respecter le rendez-vous");
}
}
}
Vous pourriez écrire cette interface sans utiliser une méthode private
en copiant le contenu de la méthode verifierHeure()
aux endroits où elle est utilisée. C’est beaucoup plus court et plus facile à lire si vous ne le faites pas. Puisque les auteurs de Java ont été assez gentils pour ajouter cette fonctionnalité pour notre commodité, autant l’utiliser!
Nous aurions pu également déclarer verifierHeure()
comme public
dans l’exemple précédent, mais cela aurait exposé la méthode pour une utilisation en dehors de l’interface. Un principe important de l’encapsulation est de ne pas exposer le fonctionnement interne d’une classe ou d’une interface lorsque ce n’est pas nécessaire. Nous couvrons l’encapsulation plus tard dans ce chapitre.
La différence entre une méthode private
non statique et une méthode static
est analogue à la différence entre une méthode d’instance et une méthode static
déclarée dans une classe. En particulier, tout concerne quelles méthodes chacune peut appeler.
Règles de Définition des Méthodes d’Interface Private
- Une méthode d’interface
private
doit être marquée avec le modificateurprivate
et inclure un corps de méthode. - Une méthode d’interface
private static
peut être appelée par toute méthode dans la définition de l’interface. - Une méthode d’interface
private
ne peut être appelée que par des méthodesdefault
et d’autres méthodesprivate
non-static
dans la définition de l’interface.
Une autre façon de penser est qu’une méthode d’interface private
n’est accessible qu’aux méthodes non-static
définies dans l’interface. Une méthode d’interface private static
, d’autre part, peut être accédée par n’importe quelle méthode dans l’interface. Pour les deux types de méthodes private
, une classe héritant de l’interface ne peut pas les invoquer directement.
Appel de Méthodes Abstract
Nous avons beaucoup parlé des nouveaux types de méthodes d’interface, mais qu’en est-il des méthodes abstract
? Il s’avère que les méthodes default
et private
non-static
peuvent accéder aux méthodes abstract
déclarées dans l’interface. C’est la principale raison pour laquelle nous associons ces méthodes à l’adhésion d’instance. Lorsqu’elles sont invoquées, il y a une instance de l’interface.
public interface RenovationZoo {
public String nomProjet();
abstract String statut();
default void afficherStatut() {
System.out.print("Le projet " + nomProjet() + " " + statut());
}
}
Dans cet exemple, nomProjet()
et statut()
ont les mêmes modificateurs (abstract et public sont implicites) et peuvent être appelés par la méthode default
afficherStatut()
.
Revue des Membres d’Interface
Nous concluons notre discussion sur les membres d’interface avec le Tableau 7.2, qui montre les règles d’accès pour les membres dans et en dehors d’une interface.
TABLEAU 7.2 Accès aux membres d’interface
Accessible depuis les méthodes default et private dans l’interface? | Accessible depuis les méthodes static dans l’interface? | Accessible depuis les méthodes dans les classes héritant de l’interface? | Accessible sans une instance de l’interface? | |
---|---|---|---|---|
Variable constante | Oui | Oui | Oui | Oui |
Méthode abstract | Oui | Non | Oui | Non |
Méthode default | Oui | Non | Oui | Non |
Méthode static | Oui | Oui | Oui (nom de l’interface requis) | Oui (nom de l’interface requis) |
Méthode private | Oui | Non | Non | Non |
Méthode private static | Oui | Oui | Non | Non |
Bien que le Tableau 7.2 puisse sembler beaucoup à mémoriser, voici quelques conseils rapides pour l’examen:
- Traitez les méthodes
abstract
,default
, etprivate
non-static
comme appartenant à une instance de l’interface. - Traitez les méthodes et variables
static
comme appartenant à l’objet de classe d’interface. - Tous les types de méthodes d’interface
private
ne sont accessibles que dans la déclaration d’interface.
En utilisant ces règles, laquelle des méthodes suivantes ne compile pas?
public interface VisiteTrainZoo {
abstract int getNomTrain();
private static void monter() {}
default void jouerCorne() { getNomTrain(); monter(); }
public static void ralentir() { jouerCorne(); }
static void accelerer() { monter(); }
}
La méthode monter()
est private
et static
, donc elle peut être accédée par n’importe quelle méthode default
ou static
dans la déclaration d’interface. La méthode getNomTrain()
est abstract
, donc elle peut être accédée par une méthode default
associée à l’instance. La méthode ralentir()
est static
, cependant, et ne peut pas appeler une méthode default
ou private
, comme jouerCorne()
, sans un objet de référence explicite. Par conséquent, la méthode ralentir()
ne compile pas.
Donnez-vous une tape dans le dos! Vous venez d’apprendre beaucoup sur les interfaces, probablement plus que vous ne pensiez possible. Maintenant, prenez une profonde respiration. Prêt? Le prochain type que nous allons couvrir est les énumérations.
La première règle devrait vous réconforter car vous ne verrez des méthodes default
que dans les interfaces. Si vous les voyez dans une classe ou une énumération à l’examen, quelque chose ne va pas. La deuxième règle dénote simplement la syntaxe, car les méthodes default
doivent utiliser le mot-clé default
. Par exemple, les extraits de code suivants ne compileront pas car ils mélangent les méthodes d’interface concrètes et abstraites:
public interface Carnivore {
public default void mangerViande(); // NE COMPILE PAS
public int getQuantiteNourritureRequise() { // NE COMPILE PAS
return 13;
}
}
Les trois règles suivantes pour les méthodes default
découlent de la relation avec les méthodes d’interface abstraites. Comme les méthodes d’interface abstraites, les méthodes default
sont implicitement public
. Contrairement aux méthodes abstraites, cependant, les méthodes d’interface default
ne peuvent pas être marquées abstract
puisqu’elles fournissent un corps. Elles ne peuvent pas non plus être marquées comme final
, car elles sont conçues pour qu’elles puissent être surchargées dans les classes implémentant l’interface, tout comme les méthodes abstraites. Enfin, elles ne peuvent pas être marquées static
puisqu’elles sont associées à l’instance de la classe implémentant l’interface.
Héritage de Méthodes default Dupliquées
La dernière règle pour créer une méthode d’interface default
nécessite quelques explications. Par exemple, quelle valeur le code suivant produirait-il?
public interface Marcher {
public default int getVitesse() { return 5; }
}
public interface Courir {
public default int getVitesse() { return 10; }
}
public class Chat implements Marcher, Courir {} // NE COMPILE PAS
Dans cet exemple, Chat
hérite des deux méthodes default
pour getVitesse()
, alors laquelle utilise-t-il? Puisque Marcher
et Courir
sont considérés comme des frères et sœurs en termes de comment ils sont utilisés dans la classe Chat
, il n’est pas clair si le code devrait produire 5 ou 10. Dans ce cas, le compilateur abandonne et dit: “Trop difficile, j’abandonne!” et échoue.
Tout n’est pas perdu, cependant. Si la classe implémentant les interfaces surcharge la méthode default
dupliquée, le code compilera sans problème. En surchargeant la méthode conflictuelle, l’ambiguïté sur quelle version de la méthode appeler a été supprimée. Par exemple, l’implémentation modifiée suivante de Chat
compilera:
public class Chat implements Marcher, Courir {
public int getVitesse() { return 1; }
}
Appel d’une Méthode default Cachée
Dans la dernière section, nous avons montré comment notre classe Chat
pouvait surcharger une paire de méthodes default
conflictuelles, mais que se passe-t-il si la classe Chat
voulait accéder à la version de getVitesse()
dans Marcher
ou Courir
? Est-elle toujours accessible?
Oui, mais cela nécessite une syntaxe spéciale.
public class Chat implements Marcher, Courir {
public int getVitesse() {
return 1;
}
public int getVitesseMarche() {
return Marcher.super.getVitesse();
}
}
C’est un domaine où une méthode default
présente des propriétés à la fois d’une méthode static
et d’instance. Nous utilisons le nom de l’interface pour indiquer quelle méthode nous voulons appeler, mais nous utilisons le mot-clé super
pour montrer que nous suivons l’héritage d’instance, pas l’héritage de classe. Notez que l’appel de Marcher.getVitesse()
ou Marcher.this.getVitesse()
n’aurait pas fonctionné. Un peu déroutant, nous le savons, mais vous devez être familier avec cette syntaxe pour l’examen.
Déclaration de Méthodes d’Interface static
Les interfaces sont également déclarées avec des méthodes static
. Ces méthodes sont définies explicitement avec le mot-clé static
et, pour la plupart, se comportent comme des méthodes static
définies dans les classes.
Règles de Définition des Méthodes d’Interface Static
- Une méthode
static
doit être marquée avec le mot-cléstatic
et inclure un corps de méthode. - Une méthode
static
sans modificateur d’accès est implicitementpublic
. - Une méthode
static
ne peut pas être marquéeabstract
oufinal
. - Une méthode
static
n’est pas héritée et ne peut pas être accédée dans une classe implémentant l’interface sans une référence au nom de l’interface.