Voici le chapitre 6 sur les classes et les interfaces en Java.
Si vous avez des remarques ou suggestions afin de le parfaire.
N’hésitez pas à me contacter via mon email:
pr.azizdarouichi@gmail.com.
Bonne lecture.
2. 2
Classes abstraites
Utilité des classes abstraites
Interfaces
Utilisation des interfaces
Interface fonctionnelle
Héritage et Interface
Interface et classe dérivée
Classe abstraite ou Interface?
Gestion des exceptions
Conflits de noms
Interface de marquage
Classes interne, locale et anonyme
Q & A
Classes abstraites
Utilité des classes abstraites
Interfaces
Utilisation des interfaces
Interface fonctionnelle
Héritage et Interface
Interface et classe dérivée
Classe abstraite ou Interface?
Gestion des exceptions
Conflits de noms
Interface de marquage
Classes interne, locale et anonyme
Q & A
Chapitre 6: Classse abstraite & Interfaces
3. Classes abstraites
Utilité :
Définir des concepts incomplets qui devront être implémentés
dans les sous-classes.
Factoriser le code.
public abstract class Forme{
protected double x, y;
public void deplacer(double dx, double dy) { // méthode concrète
x += dx;
y += dy;
}
public abstract double perimetre(); // méthode abstraite
public abstract double surface();
}
3
4. Classes abstraites
Classe abstraite : classe non instanciable, c'est à dire qu'elle
n'admet pas d'instances directes.
Impossible de faire new ClasseAbstraite(…);
Méthode abstraite : méthode n'admettant pas d'implémentation
au niveau de la classe dans laquelle elle est déclarée, on ne peut pas dire
comment la réaliser.
Une classe pour laquelle au moins une méthode abstraite est
déclarée, alors elle est une classe abstraite (l'inverse n'est pas vrai).
Les méthodes abstraites sont particulièrement utiles pour mettre
en œuvre le polymorphisme.
l'utilisation du nom d'une classe abstraite comme type pour une (des)
référence(s) est toujours possible (et souvent souhaitable !!!).
4
5. Classes abstraites
Une classe abstraite est une description d'objets destinée à être
héritée par des classes plus spécialisées.
Pour être utile, une classe abstraite doit admettre des classes
descendantes concrètes.
Toute classe concrète sous-classe d'une classe abstraite doit
“concrétiser” toutes les méthodes abstraites de cette dernière.
Une classe abstraite permet de regrouper certaines caractéristiques
communes à ses sous-classes et définit un comportement minimal
commun.
La factorisation optimale des propriétés communes à plusieurs
classes par généralisation nécessite le plus souvent l'utilisation de
classes abstraites.
5
17. Interfaces
Toutes les méthodes sont abstraites.
Possibilité d’héritage de plusieurs interfaces.
Les interfaces permettent de s’affranchir d’éventuelles contraintes
d’héritage.
Lorsqu’on examine une classe implémentant une ou plusieurs
interfaces, on est sûr que le code d’implémentation est dans le corps
de la classe. Excellente localisation du code (défaut de l’héritage
multiple).
Permet une grande évolutivité du modèle objet.
17
19. Interfaces
19
Du point de vue syntaxique, la définition d’une interface est proche
de celle d’une classe abstraite.
Il suffit de remplacer les mots-clés abstract class par le mot-clé
interface:
public interface Printable{
public void print();
}
Une fois l’interface déclarée, il est possible de déclarer des variables
de ce nouveau type:
Printable document;
Printable[] printSpool;
22. Utilisation des interfaces
22
En Java, une classe ne peut hériter que d’une et d’une seule classe
parente (héritage simple).
Une classe peut, par contre, implémenter une ou plusieurs
intefaces en utilisant la syntaxe suivante:
implements interface1, interface2, …
L’implémentation d’une ou plusieurs interfaces (implements) peut
être combinée avec l’heritage simple (extends).
La clause implements doit suivre la clause extends.
23. Utilisation des interfaces
23
Exemples:
public class Report implements Printable{…}
public class Book implements Printable, Zoomable{…}
public class Circle extends Shape implements Printable{…}
public class Square extends Rectangle
implements Printable, Zoomable{…}
26. Utilisation des interfaces
26
Exemple
Tous les objets dont la classe implémente l’interface Comparable pourront
ensuite être triés.
public class Person implements Comparable{
private int age;
…
public int compareTo(Object p){
if (p instanceof Person ){
if (this.age<((Person)p).age) return -1;
if (this.age>((Person)p).age) return 1;
return 0;
} else …
}
…
}
31. Utilisation des interfaces
31
Interfaces et constantes
L’essentiel du concept d’interface réside dans les en-têtes de
méthodes qui y figurent.
Mais une interface peut aussi renfermer des constantes symboliques
qui seront alors accessibles à toutes les classes implémentant
l’interface :
interface I{
void f(int n) ;
void g() ;
static final int MAXI = 100 ;
}
class A implements I{
// doit définir f et g
// dans toutes les méthodes de A, on a accès au symbole MAXI :
// par exemple : if (i < MAXI) .....
}
32. Interfaces
32
Interfaces et constantes
Exemple 2:
public interface Constantes { // dans le fichier Constantes.java
public static final double G = 9.81;
}
public class ChampGravitationnel // dans le fichier ChampGravitationnel.java
implements Constantes {
private double vitesse ;
public double calculeVitesse(double temps) {
return G*temps ;
}
}
33. Utilisation des interfaces
33
Dérivation d’une interface
On peut définir une interface comme une généralisation d’une autre. On
utilise là encore le mot-clé extends, ce qui conduit à parler d’héritage ou
de dérivation, et ce bien qu’il ne s’agisse en fait que d’emboîter
simplement des déclarations :
interface I1{
void f(int n) ;
static final int MAXI = 100 ;}
interface I2 extends I1{
void g() ;
static final int MINI = 20 ;}
En fait, la définition de I2 est totalement équivalente à :
interface I2{
void f(int n) ;
void g() ;
static final int MAXI = 100 ;
static final int MINI = 20 ;
}
34. Utilisation des interfaces
34
Conflits de noms
Considérons les deux interfaces suivantes I1 et I2:
interface I1{
void f(int n) ;
void g() ;
}
interface I2{
void f(float x) ;
void g() ;
}
class A implements I1, I2{
// A doit définir deux méthodes f : void f(int) et void f(float),
// mais une seule méthode g : void g()
}
35. Utilisation des interfaces
35
Conflits de noms
Considérons ces deux interface A et B:
public interface A {
default void a() {
// implémentation
}
}
public interface B {
default void a() {
// implémentation
}
}
public class C implements A, B {
// !!! erreur de compilation !!!
// corps de la classe
}
36. Utilisation des interfaces
36
Conflits de noms
Construisons une instance de C et invoquons la méthode a().
La question qui se pose : quelle implémentation de la méthode a()
va-t-elle être invoquée ?
Celle de A ?
Celle de B ?
37. Utilisation des interfaces
37
Conflits de noms
Pour corriger cette erreur, il suffit tout simplement de lever
l'ambiguïté. Nous avons deux solutions pour ce faire.
Créer une implémentation concrète de la méthode a() dans la
classe C. Une implémentation concrète a toujours la priorité sur
une implémentation par défaut.
Décider qu'une des deux interfaces, par exemple A étend la seconde.
Dans ce cas, A devient plus spécifique que B, et l'on dit que c'est
l'implémentation la plus spécifique qui a la priorité.
38. Utilisation des interfaces
38
Conflits de noms
Remarque:
En cas de conflit l'implémentation concrète gagne contre
l'implémentation par défaut, et l'implémentation par défaut plus
spécifique gagne contre une moins spécifique.
39. L’interface Cloneable
39
La classe Object possède une méthode clone() protégée qui se
contente d’effectuer une copie superficielle de l’objet. L’idée des
concepteurs de Java est que cette méthode doit être redéfinie dans
toute classe clonable.
Il existe une interface très particulière Cloneable. Ce nom
s’emploie comme un nom d’interface dans une clause implements.
Mais, contrairement à ce qui se produirait avec une interface
usuelle, cette clause n’impose pas la redéfinition de la méthode
clone().
La déclaration:
class X implements Cloneable
mentionne que la classe X peut subir une copie profonde par appel de
la méthode clone(). Cette dernière peut être celle de Object ou une
méthode fournie par X.
40. Utilisation des interfaces
40
L’interface Cloneable
L’en-tête de la méthode clone() est :
protected Object clone()
Cela signifie que son utilisation nécessite toujours un cast de son
résultat dans le type effectif de l’objet soumis à copie.
La syntaxe pour invoquer cette méthode est la suivante:
Object copy = obj.clone();
ou
MaClasse copie = (MaClasse) obj.clone();
41. Utilisation des interfaces
41
L’interface Cloneable
Exemple
class Personne implements Cloneable{ // implémentation de l'interface Cloneable
private String nom; private String prenom; private int age;
public Personne(String unNom, String unPrenom, int unAge){
nom = unNom; prenom = unPrenom; age = unAge; }
public Object clone() throws CloneNotSupportedException{
return super.clone(); }
public static void main(String[] atgs){
Personne myStudent = new Personne("MyStudent","SIR", 21);
Personne myStudentClone = null;
try{
myStudentClone = (Personne) myStudent.clone(); // il faut réaliser un cast car la méthode clone renvoie un Object.
}catch(Exception e) {
System.out.println("Erreur");
}
System.out.println(myStudent.nom + " " + myStudent.prenom+" " + myStudent.age);
System.out.println(myStudentClone.nom + " " + myStudentClone.prenom+" " +
myStudentClone.age);
}
}
47. Interface fonctionnelle
47
Une interface fonctionnelle peut avoir des méthodes par défaut,
comme le montre l‘exemple ci-dessous :
Exemple:
@FunctionalInterface
public interface StringComparator {
int compare(String s1, String s2);
default String sayHello( ){
return “ Hello world ”;
};
}
49. Interface et classe dérivée
49
La clause implements est une garantie qu’offre une classe
d’implémenter les fonctionnalités proposées dans une interface.
Elle est totalement indépendante de l’héritage; autrement dit, une
classe dérivée peut implémenter une interface ou plusieurs interfaces.
50. Interface et classe dérivée
50
Exemple:
interface I{
void f(int n) ;
void g() ;
}
class A {
.....
}
class B extends A implements I{
// les méthodes f et g doivent soit être déjà définies dans A,
// soit définies dans B
}
52. Classe abstraite ou Interface?
52
Interface
Que les types des traitements
(signatures des méthodes)
Vue utilisateur
Classe abstraite
Propriétés et méthodes concrètes
Certaines méthodes doivent être
codées par d’autres.
Vue développeur
55. Conflits de noms
55
Exemple:
Considérons les deux interfaces suivantes I1 et I2 :
interface I1{
void f(int n);
void g();
}
interface I2{
void f(float x);
int g();
}
class A implements I1, I2{
// pour satisfaire à I1 et I2, A devrait contenir à la fois une méthode
// void g() et une méthode int g(), ce qui n’est pas possible
// d’après les règles de redéfinition
}
Cette fois, une classe ne peut implémenter à la fois I1 et I2.
57. Conflits de noms
57
Notons qu'une implémentation peut appeler explicitement une
implémentation par défaut:
Exemple:
public interface A {
default void a() {
// implémentation
}
}
public class C implements A {
public void a() {
return A.super.a(); // appelle la méthode par défaut de A
}
}
59. Interface de marquage
59
Interface Cloneable
C’est une interface de marquage (ou balisage) : une classe qui
implémente l’interface Cloneable, indique à Object.clone() qu’il
est légal pour cet objet de faire une copie superficielle attribut par
attribut pour les instance de cette classe.
Une tentative de clonage pour des classes qui n’implémentent
pas Cloneable se traduit par la levée d’une exception:
CloneNotSupportedException.
60. Interface Cloneable
60
Par convention, les classes qui implémentent cette interface doit
redéfinir Object.clone() (qui est protected) avec une méthode
public.
Rappel:
La définition de clone() pour la classe Object est :
protected native Object clone() throws CloneNotSupportedException
61. 61
Plus de détails sur l'interface Cloneable voir le lien suivant:
https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Cloneable.html
Interface Cloneable
62. Interface de marquage
62
Interface Serializable
La sérialisation d'un objet est le processus de sauvegarde d'un
objet complet sur fichier, d'où il pourra être restauré à tout
moment.
Le processus inverse de la sauvegarde (restauration) est connu
sous le nom de désérialisation.
63. Interface Serializable
63
L'interface Serializable est une interface de marquage (ou balisage).
Cette interface ne comporte aucune méthode, sa seule fonction est
de marquer les classes qui pourront être "sérialisées", c'est-à-dire
dont les instances pourront être écrites dans des fichiers ou
transmises via un réseau.
Dans le jargon des développeurs Java, sérialiser un objet consiste à
le convertir en un tableau d'octets, que l'on peut ensuite écrire dans
un fichier, envoyer sur un réseau au travers d'une socket etc...
64. 64
Plus de détails sur l'interface Serializable voir le lien suivant:
https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/io/Serializable.html
Interface Serializable
65. Classes internes
65
Java permet de définir une classe dite interne à l’intérieur d’une
autre classe.
La notion de classe interne correspond à cette situation:
class Englobante //définition d’une classe usuelle
{
… //méthodes et attributs de la classe Englobante
class Englobee{ //définition d’une classe interne à la classe Englobante
. . . //méthodes et attributs de la classe Englobee
}
. . . //autres méthodes et attributs de la classe Englobante
}
66. Classes internes
66
Exemple 1:
class Externe{
public void f() { // méthode de la classe Externe
Interne i = new Interne(); // création d’un objet de type Interne ;
// sa référence est ici locale à la méthode f
}
class Interne{
.....
}
.....
}
67. Classes internes
67
Exemple 2:
class Externe{
private Interne i1, i2 ; // les champs i1 et i2 de Externe sont des références
// à des objets de type Interne
class Interne{
.....
}
.....
}
68. Classes internes
68
Lien entre objet interne et objet externe
Une classe interne peut avoir toujours accès aux méthodes et
attributs (même privés) de la classe englobante/externe.
Une classe englobante/externe peut avoir toujours accès aux
méthodes et attributs (même privés) d’une classe interne.
Un objet d’une classe interne est toujours associé, au moment de
son instanciation, à un objet d’une classe externe.
69. Classes internes
69
Lien entre objet interne et objet externe
Exemple 1
class Externe {
int e;
class Interne{
. . .
Externe.this.e = 18; // ici, on a accès au champ e de l’objet de classe Externe associé à l’objet
//courant de classe Interne
}
. . .
}
70. Classes internes
70
Lien entre objet interne et objet externe
Exemple 1/2:
class Externe{
private int ne ; // champ privé de Externe
private Interne i1, i2 ; // les champs i1 et i2 de Externe sont des références à des objets de type Interne
public void fe(){
Interne i = new Interne() ; // création d’un objet de type Interne, associé à l’objet de classe Externe
} // lui ayant donné naissance (celui qui aura appelé la méthode fe())
public void g (){
..... // ici, on peut accéder non seulement à i1 et i2, mais aussi à i1.ni ou i2.ni
}
class Interne{
private int ni ;
public void fi(){
..... // ici, on a accès au champ ne de l’objet de classe Externe associé à l’objet courant de classe Interne
}
…}
…
}
71. Classes internes
71
Lien entre objet interne et objet externe
Exemple 2/2:
Externe e1 = new Externe(), e2 = new Externe();
e1.fe();
e2.fe();
74. Classes internes
74
Déclaration et instanciation d’un objet d’une classe interne
Il faut toujours rattacher un objet d’une classe interne à un objet de
sa classe englobante, moyennant l’utilisation d’une syntaxe
particulière de new.
public class E { // classe englobante de I
.....
public class I { // classe interne à E
.....
}
.....
}
75. Classes internes
75
Déclaration et instanciation d’un objet d’une classe interne
A l’extérieur de E, on peut déclarer une référence à un objet de
type I:
E.I i; // référence à un objet de type I (interne à E)
La création d’un objet de type I ne peut se faire qu’en le rattachant
à un objet de sa classe englobante.
Par exemple, si l’on dispose d’un objet e créé ainsi:
E e = new E() ;
On pourra affecter à i la référence à un objet de type I, rattaché à e,
en utilisant new comme suit :
i = e.new I(); // création d’un objet de type I, rattaché à l’objet e
// et affectation de sa référence à i
76. Classes internes statiques
76
Java permet de créer des classes internes "autonomes" en employant
l’attribut static.
Exemple:
public class E { // classe englobante
.....
public static class I { // définition (englobée dans celle de E)
..... // d’une classe interne autonome
}
}
77. Classes internes statiques
77
Exemple:(suite)
Depuis l’extérieur de classe E, on peut instancier un objet de classe
I de cette façon:
E.I i = new E.I();
L’objet i n’est associé à aucun objet de type E.
La classe I n’a plus accès aux membres de E, sauf s’il s’agit de
membres statiques.
78. Classes internes
78
Classe interne static Classe interne non static
public class E {
int a = 21;
static class I{
int b = 12;
}
}
public class E {
int a = 21;
class I{
int b = 12;
}
}
E e = new E();
E.I i = new E.I();
E e = new E();
E.I i = e.new I();
Classe interne static/Classe interne non static
79. Classes internes
79
Remarque:
Lors de la compilation, le compilateur génère un fichier constitué
du nom de la classe englobante puis un ‘$’, puis le nom de la
classe interne, puis “.class”.
Par exemple, pour l’exemple précédent:
Génère E$I.class
80. Classes internes locales
80
Une classe interne définie dans un bloc est une classe interne dont
la portée est limitée au bloc : c’est une classe interne locale.
Une classe locale ne peut pas être static.
Une classe locale peut accéder aux attributs de la classe englobante
ainsi qu'aux paramètres et variable locales de la méthode où elle est
définie, à condition que ceux-ci soit spécifiés final.
81. Classes internes locales
81
Par exemple, la définition d’une classe interne I dans une
méthode f() d’une classe E.
L’instanciation d’objets de type I ne peut se faire que dans f.
Un objet de type I a alors accès aux variables locales finales de f.
82. Classes internes locales
82
Exemple:
public class E{
.....
void f(){
final int n=5 ;
double x ;
class I { // classe interne à E, locale à f
..... // ici, on a accès à n, pas à x
}
I i = new I() ; // classique
}
}
83. Classes internes locales
83
Remarque:
Lors de la compilation, le compilateur génère un fichier constitué
du nom de la classe englobante puis un ‘$’, puis un nombre,
puis le nom de la classe interne, puis “.class”
Par exemple, pour l’exemple précédent:
Génère E$1I.class
84. Classes anonymes
84
Java permet de définir ponctuellement une classe, sans lui donner
de nom.
Il permet de déclarer une classe et de créer un objet de celle-ci en
une expression.
La classe anonyme est un sous-type d'une interface ou d'une
classe abstraite ou concrète.
85. Classes anonymes
85
Définition
Il s’agit de classes dérivées ou implémentant une interface. La
syntaxe de définition d’une classe anonyme ne s’applique que dans
deux cas :
classe anonyme dérivée d’une autre,
classe anonyme implémentant une interface.
87. Classes anonymes
87
Dérivation de super classe
Exemple 1/2:
Supposons que l’on dispose d’une classe A. Il est possible de créer un
objet d’une classe dérivée de A, en utilisant une syntaxe de cette
forme :
A a;
a = new A() {
// champs et méthodes qu’on introduit dans
// la classe anonyme dérivée de A
} ;
88. Classes anonymes
88
Dérivation de super classe
Exemple 2/2:
Tout se passe comme si l’on avait procédé ainsi :
A a ;
class A1 extends A {
// champs et méthodes spécifiques à A1
} ;
.....
a = new A1();
Cependant, dans ce dernier cas, il serait possible de définir des
références de type A1, ce qui n’est pas possible dans le premier cas.
89. Classes anonymes
89
Programme complet
class A{
public void affiche() {
System.out.println ("Je suis un A") ;
}
}
public class Anonym1{
public static void main (String[] args){
A a ;
a = new A() {
public void affiche (){
System.out.println ("Je suis un anonyme dérivé de A") ;
}
} ;
a.affiche() ;
}
}
Résultat: Je suis un anonyme dérivé de A
91. Classes anonymes
91
Implémentation d’interface
Exemple 2
interface Affichable{
public void affiche() ;
}
public class Anonym2{
public static void main (String[] args){
Affichable a ;
a = new Affichable(){
public void affiche (){
System.out.println ("Je suis un anonyme implémentant Affichable") ;}
} ;
a.affiche() ;
}
}
Résultat: Je suis un anonyme implémentant Affichable
92. Classes anonymes
92
Utilisation de la référence à une classe anonyme
Dans les précédents exemples, la référence de la classe anonyme
était conservée dans une variable (d’un type de base ou d’un type
interface).
On peut aussi la transmettre en argument d’une méthode ou en
valeur de retour.
93. Utilisation de la référence à une classe anonyme
93
Exemple:
interface I{
.....
}
public class Util{
public static f(I i) {
.....
}
}
.....
f(new I() {
// implémentation des méthodes de I
} ) ;
94. Classes anonymes
94
Les classes anonymes possèdent des restrictions :
elles ne peuvent pas être déclarées abstract;
elles ne peuvent pas non plus être déclarées static;
elles ne peuvent pas définir de constructeur;
elles sont automatiquement déclarées final: l'héritage est donc
impossible!
95. Classes anonymes
95
Utilisation de la référence à une classe anonyme
Remarque:
L’utilisation des classes anonymes conduit généralement à des
codes peu lisibles.
On la réservera à des cas très particuliers où la définition de la
classe anonyme reste brève.