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

modifier

En 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

modifier

Dé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

modifier

Si, 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

modifier

Une 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

modifier

L'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.