Programmation Java/Interfaces
Une interface définit un ensemble de méthodes mais pas leur implémentation. C'est pourquoi il n'est pas possible d'instancier une interface directement. Il est toutefois possible d'appeler une méthode en utilisant une référence à une interface, sans savoir quelle classe implémentant concrètement la méthode appelée est réellement utilisée.
Présentation
modifierEn fait, une interface est une classe abstraite dont toutes les méthodes sont abstraites et dont tous les attributs sont constants (des constantes, voir le mot-clé final).
- Liste de méthodes dont on donne seulement la signature ;
- Représente un "contrat", ce qu'on attend d'un objet ;
- Peut être implémentée par une ou plusieurs classes qui doivent donner une implémentation pour chacune des méthodes annoncées (et éventuellement d'autres) ;
- Une classe peut implémenter plusieurs interfaces (permettant un héritage multiple, en les séparant par des virgules après le mot implements) ;
- Toutes les méthodes d'une interface sont implicitement abstraites ;
- Une interface n'a pas de constructeurs ;
- Une interface ne peut avoir de champs sauf si ceux-ci sont statiques ;
- Une interface peut être étendue par une ou plusieurs autre(s) interface(s).
Exemple
modifierDéfinition de l'interface ;
package org.wikibooks.fr;
public interface Vehicule
{
public void rouler(int vitesse);
public void freiner();
public int getVitesse();
}
La présence du modificateur public
est implicite et n'est donc pas obligatoire.
Cependant, sa présence est recommandée et permet de montrer que ces méthodes seront publiques, de plus cela permet de copier la signature de la méthode dans la classe qui l'implémente.
La présence du modificateur abstract
est implicite également.
Sa présence n'est cependant pas recommandée afin de permettre la copie de la signature de la méthode dans la classe qui l'implémente.
On a défini ici ce qu'on attend d'un objet de type véhicule.
On peut maintenant donner une ou plusieurs implémentations de cette interface grâce à l'utilisation du mot clef implements :
package org.wikibooks.fr;
public class Velo implements Vehicule
{
// Champs
private String marque;
private int rayonRoue, vitesse;
// Constructeurs
public Velo(String marque, int rayonRoue)
{
this.marque = marque;
this.rayonRoue = rayonRoue;
}
// Méthodes
public int getVitesse()
{
// Retourner la vitesse actuelle du vélo
return vitesse;
}
public void rouler(int vitesse)
{
// Coder ici la manière dont le vélo roule
if (vitesse < 0 || vitesse > 80) throw new IllegalArgument("Vitesse incorrecte pour un vélo.");
this.vitesse = vitesse;
}
public void freiner()
{
// Coder ici la manière dont le vélo freine
this.vitesse = 0;
}
// ... Autres méthodes propres à Velo
}
package org.wikibooks.fr;
public class Auto implements Vehicule
{
//Champs
private String marque;
private int poids, vitesse;
// Constructeurs
public Auto(String marque, int poids)
{
this.marque = marque;
this.poids = poids;
}
// Méthodes
public int getVitesse()
{
// Retourner la vitesse actuelle de l'auto
return vitesse;
}
public void rouler(int vitesse)
{
//Coder ici la manière dont l'auto roule
if (vitesse < 0 || vitesse > 160) throw new IllegalArgument("Vitesse incorrecte pour une auto.");
this.vitesse = vitesse;
}
public void freiner()
{
// Coder ici la manière dont l'auto freine
this.vitesse = 0;
}
// ... Autres méthodes propres à Auto.
}
Dans cet exemple, nous avons donné deux implémentations de Vehicule.
Conséquences :
- Ces 2 objets peuvent être vus comme des véhicules, c'est ce qu'on appelle le polymorphisme.
- Partout où on attend un objet de type Vehicule, on peut mettre un de ces deux objets.
- Par ce biais, on introduit une couche d'abstraction dans notre programmation ce qui la rend beaucoup plus flexible.
Abstraction
modifierSi, par exemple, nous avons une classe Personne possédant une méthode conduire(Vehicule v), on peut alors écrire :
Personne p = new Personne();
p.conduire(new Velo()); //comme la méthode attend un Vehicule en argument, on peut passer tout objet implémentant cette interface.
p.conduire(new Auto()); //idem
On peut "instancier" un Vehicule
par le biais de ses implémentations :
Vehicule v = new Auto();
Vehicule t = new Velo();
Dans ce cas v et t sont vus comme des Vehicule
et, par conséquent, on ne peut appeler sur ces objets que les méthodes définies dans l'interface Vehicule
.
Implémentation partielle
modifierUne classe peut n'implémenter qu'une partie de l'interface.
Dans ce cas, il s'agit d'une classe abstraite et doit utiliser le mot-clé abstract
.
Dans l'exemple de ce chapitre, les deux classes concrètes implémentent certaines méthodes de la même façon. Ce comportement commun peut être déplacé dans une classe abstraite intermédiaire :
package org.wikibooks.fr;
public abstract class VehiculeConcret implements Vehicule
{
// Attributs accessible par les sous-classes
protected int vitesse;
// Méthodes implémentant l'interface Vehicule
public int getVitesse()
{
// Retourner la vitesse actuelle du véhicule concret
return vitesse;
}
public void freiner()
{
this.vitesse = 0;
}
// La méthode rouler(int vitesse) n'est pas implémentée par cette classe abstraite.
}
package org.wikibooks.fr;
public class Velo extends VehiculeConcret
{
// Champs
private String marque;
private int rayonRoue, vitesse;
// Constructeurs
public Velo(String marque, int rayonRoue)
{
this.marque = marque;
this.rayonRoue = rayonRoue;
}
// Méthodes complétant l'implémentation de l'interface Vehicule
public void rouler(int vitesse)
{
// Coder ici la manière dont le vélo roule
if (vitesse < 0 || vitesse > 80) throw new IllegalArgument("Vitesse incorrecte pour un vélo.");
this.vitesse = vitesse;
}
// ... Autres méthodes propres à Velo
}
package org.wikibooks.fr;
public class Auto extends VehiculeConcret
{
//Champs
private String marque;
private int poids, vitesse;
// Constructeurs
public Auto(String marque, int poids)
{
this.marque = marque;
this.poids = poids;
}
// Méthodes complétant l'implémentation de l'interface Vehicule
public void rouler(int vitesse)
{
//Coder ici la manière dont l'auto roule
if (vitesse < 0 || vitesse > 160) throw new IllegalArgument("Vitesse incorrecte pour une auto.");
this.vitesse = vitesse;
}
// ... Autres méthodes propres à Auto.
}
Instanciation
modifierL'exemple suivant semble instancier l'interface :
Vehicule quelqu_un_en_rollers = new Vehicule()
{
private int vitesse = 0;
// Méthodes
public int getVitesse()
{
// Retourner la vitesse actuelle
return vitesse;
}
public void rouler(int vitesse)
{
if (vitesse < 0 || vitesse > 20) throw new IllegalArgument("Vitesse incorrecte pour des rollers.");
this.vitesse = vitesse;
}
public void freiner()
{
// Coder ici la manière dont l'auto freine
this.vitesse = 0;
}
}; // Fin de déclaration d'objet.
En réalité, une classe anonyme est créée et définie, implémentant l'interface indiquée.
Cette syntaxe est utilisée notamment pour créer des instances de listeners pour l'écoute d'évènements, comme en Swing.