Programmation par les Objets en Java
Interfaces et Classes Abstraites (TD5)
Najib Tounsi
(Lien permanent: http://www.mescours.ma/Java/TD/tdJava5.html
(.pdf
))
Dans ce TD seront vues les interfaces et leur réalisation ainsi les classes abstraites. Sera discuté aussi la nuance entre ces deux concepts.
Sommaire
Une interface est une collection nommée de déclarations de méthodes (sans les implémentations). Une interface peut aussi déclarer des constantes.
static
.interface Loisir { public int distance = 21; public void courirOuMarcher(); }
interface
au lieu de class
. distance
considérée comme final variable et ne
peut être modifiée.La réalisation (ou "implémentation") d'une interface, c'est définir des méthodes déclarées. Elle doit être faite dans une classe séparée. Voici un exemple de classe qui implémente l'interface ci-dessus :
class Coureur implements Loisir {
//Implémentation de la méthode courirOuMarcher
public void courirOuMarcher(){ System.out.println("Je cours "+distance+" Km."); }
};
Noter le mot clé implements.
Exemple d'utilisation :
class Test{
static public void main(String args[]){
Coureur c = new Coureur() ; c.courirOuMarcher();
}
}
Compiler l'interface et ensuite ces deux classes. Exécuter le teste.
Résultat :
Je cours 21 Km.
Exercices :
Loisir
et
l'instancier avec avec un objet Coureur
? C'est à dire :(a)
Loisir c = new Coureur();
au lieu de
(b)
Coureur c = new Coureur();
Vérifier que c'est possible. Vérifier aussi que :
(c)
Loisir l = new Loisir(); // :-(
par contre, n'a pas de sens. On n'instancie pas une interface,
car les méthodes n'y sont pas définies. Ne pas confondre avec la ligne
(a) où l'instance est un
objet Coureur
.
Coureur
la méthode :public void courirMoins() {
System.out.println("Je cours "+(distance/2)+" Km.");
}
et dans main, rajouter l'appel :
c.courirMoins();
Vérifier cet appel dans les deux cas (a) et (b) ci-dessus.
Réponse: Erreur de compilation dans le cas où c
est de type Loisir
. Normal, puisque la méthode courirMoins
n'est pas déclarée dans l'interface.
final
, et ne peut être
modifiée ni dans la classe Coureur
ni ailleurs. Le
vérifier en faisant :distance /= 2;
dans la méthode CourirMoins
.
Ailleurs, l'accès à la constante, en lecture donc, peut se faire soit
par la notation c.distance
soit par Loisir.distance
tout simplement.
Créer une autre classe Marcheur
, qui implémente la même
interface Loisir
. L'implémentation affichera un simple
message "Moi, je marche...
".
mesLoisirs
d'objets Loisir
, et qui l'instancie indifféremment avec
des objets de type Coureur
ou Marcheur
.
Loisir mesLoisirs[] = { new Marcheur(), new Coureur() } ;
coureurOuMarcher()
sur les objets du tableau donne le message correspondant à chaque objet.
(Se contenter d'un tableau deux éléments.)Concevoir deux interfaces A et B et une classe C qui réalise (implémente) ces deux interfaces.
syntaxe: class C implements A, B { corps de la classe..}
Que se passe t-il si les deux interfaces A et B déclarent une même méthode f()? Une même constante x?
Indication: pour le savoir, créer une classe Test qui utilise f() et x.
Pourquoi ne peut-on
utiliser x? (réponse:
La définition de x est donnée
dans les interfaces A
et B et non dans les classes qui les réalisent. Il faudrait
donc préfixer x par le nom de l'interface (A.x
ou
B.x
)).
Par contre, pour la méthode f() il n'y pas de problème. Elle
est définie dans la classe qui implémente. Si on déclare
C c = new C();
il n'y a pas de problème à écrire c.f();
.
C'est un appel de méthode normal. Même si on déclare A c
= new C();
, c.f();
a toujours le même sens.
default void g() {};
C'est une définition par défaut donnée dans l'interface. Les classes qui réalisent l'interface pourront alors la redéfinir si elles le souhaitent. Bénéfice : on peut augmenter les fonctionnalités d'un certains nombre de classes sans toucher à leur code. On ajoute simplement une méthode par défaut dans l'interface qu'elles implémentent. Les classes pourront implémenter la nouvelle méthode plus tard.
Une classe abstraite est une classe qui peut contenir des méthodes sans corps, dites méthodes abstraites. L'implantation est laissée (déléguée) à de futures sous classes de la classe abstraite.
Une classe abstraite ne contient pas forcément des méthodes abstraites. Le fait qu'une classe soit abstraite, implique qu'on ne peut pas en créer des instances. Il faut alors en dériver des sous classes pour pouvoir instancier des objets et leur appliquer des méthodes.
Mais une classe qui contient une méthode abstraite doit être déclarée abstraite. De même, une sous-classe qui ne fournit pas l'implantation d'une méthode abstraite héritée (déclarée dans une classe mère), doit être déclarée abstraite à son tour.
Exemple : Illustration de ces mécanisme.
abstract class Generale {
public int x=2; // x variable d'instance (non considérée static)
abstract public void qui(); // méthode abstraite à implémenter par les sous-classes
public void moi(){ System.out.println("Méthode générale"); } }
NB. Une méthode sans corps doit toujours être déclarée abstraite. Par ailleurs, une méthode abstraite ne peut être déclarée static (pourquoi?)
Implantation :
class Speciale1 extends Generale{
public void qui() { // Implementation de qui() System.out.println("C'est la sous-classe Speciale1"); } }
Noter extends
(vs. implements
).
Test :
class Test{ static public void main(String args[]){ Speciale1 s = new Speciale1(); s.moi(); s.qui(); s.x++; System.out.println(s.x); } }
NB. Compiler d'abord la classe Generale
.
Résultat obtenue :
$ java Test
Méthode générale // méthode moi héritée exécutée C'est la sous-classe Speciale1 // méthode qui propre exécutée 3 // x incrémentée
Exercices:
Speciale1
une redéfinition de la
méthode moi
déjà définie dans la classe Generale
(afficher un message approprié dans cette redéfinition). Refaire
le teste pour vérifier que ce nouveau message s'affiche la place
du message "Méthode générale
" précédent.Object
. Des classes sans aucun rapport
entre elles peuvent implanter la
même interface. Alors que les classes qui dérivent d'une même classe
abstraite appartiennent à une même famille. (On reviendra sur cet aspect
en fin de TD7-bis) interface C extends A , B
{}
).En réalité, à part les différences de forme, une interface est une spécification (abstraite) appelée à être implémentée (concrétisée) éventuellement de plusieurs façons par plusieurs classes différentes, chacune à sa manière. Le fait ici, est que ces classes sont quelconques et n'ont aucune relations entre elles. C'est une vision génie logiciel plus générale.
Dans le cas des classes abstraites, les classes qui implémentent l'abstraction sont des sous-classes. C'est plus une vision classification et développement progressif et structuré.
Extrait de la documentation Sun Implementing
an interface
allows a class to become more formal about the behavior it promises to
provide. Interfaces form a contract between the class and the outside
world, and this contract is enforced at build time by the compiler. If
your class claims to implement an interface, all methods defined by
that interface must appear in its source code before the class will
successfully compile.
(http://docs.oracle.com/javase/tutorial/java/concepts/interface.html)
(A partir de Java 8, une interface peut fournir une implémentation par défaut pour une méthode. Les classes qui réalisent l'interface peuvent redéfinir ou pas cette méthode. Si elles ne ne la redéfinissent pas, c'est la méthode donnée dans l'interface qui sera invoquée par les classes appelantes.
That's all folks
Revision : Avril 2020