Programmer avec des objets

153
Programmer avec des objets (notes provisoires, janvier 2002) Etienne Vandeput © CeFIS - FUNDP

Transcript of Programmer avec des objets

Page 1: Programmer avec des objets

Programmer avec des objets(notes provisoires, janvier 2002)

Etienne Vandeput© CeFIS - FUNDP

Page 2: Programmer avec des objets

Ces notes sont celles de la formation organisée en 2002 sur le thème précité. Elles sont doncdestinées à être remaniées dans un futur proche en fonction des observations, corrections,remarques, commentaires que vous êtes invités à y apporter.

Adresse de contact: [email protected]

Page 3: Programmer avec des objets

Table des matières

Chapitre 1 Pourquoi programmer avec des objets? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Le choix d’un langage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

L’installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

La machine Java virtuelle (JVM) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

Chapitre 2 Des objets qui dialoguent... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

Une approche naturelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

L’encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

Un exemple simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Les classes d’objets et les mécanismes de la POO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

Exercice unique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

Chapitre 3 Java: Notions de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Avertissement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Définition d’une classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

La classe Point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

Les types primitifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Les méthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Les membres de classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

Les constructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

La déclaration des variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

La méthode main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

L’exécution du programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

Une application qui crée des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

Objet vs variable objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

La référence “this” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

Le traitement des erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

Chapitre 4 Quelques compléments utiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

Le passage des paramètres en Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

Page 4: Programmer avec des objets

Premier point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

Second point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

La classe String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

La méthode toString() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Les méthodes statiques et les variables de classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

Les champs et les méthodes déclarés finals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

Les opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

Les opérateurs arithmétiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

Les opérateurs logiques et de comparaison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

Les opérateurs d’incrémentation, de décrémentation et d’affectation élargie . . . . . 48

L’opérateur conditionnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

Les commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

Les tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Entrées et sorties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

Les conversions automatiques et le transtypage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

Exercices de compréhension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

Chapitre 5 Java: héritage et polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

Spécialisation et généralisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

Intérêt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

Héritage, surcharge et remplacement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

Syntaxe en Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

Le modificateur d’accès “protected” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

Constructeurs et héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

Polymorphisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

Intérêt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

Mécanisme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

Chapitre 6 Les structures de contrôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

Utilité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

Page 5: Programmer avec des objets

Les structures alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

Alternative simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

Alternative multiple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

Les structures répétitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

La boucle “while” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

La boucle “do - while” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

La boucle “for” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

“break”, “label”, “continue” et autre “return” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

Chapitre 7 D’autres mécanismes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

Les classes abstraites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

Les interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87

Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

Héritage multiple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

Héritage multiple et interfaces prédéfinies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Les interfaces graphiques et la gestion des événements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Quelques outils . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

Le garbage collector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

Les packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

Déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

Les noms de packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

Accès . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

Hiérarchie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

Stockage des classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

La recherche des classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

Chapitre 8 GUI et gestion des événements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

Le modèle des événements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

AWT et Swing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

Que de hiérarchies! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

Page 6: Programmer avec des objets

Interfaces et programmation événementielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

Le détecteur de multiples de 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

Première version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

Deuxième version: contrôle de fermeture de la fenêtre . . . . . . . . . . . . . . . . . . . . . . 113

Troisième version: classe anaonyme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

Quatrième version: autre système d’écoute et traitement des exceptions . . . . . . . . 114

Cinquième version: une mise à jour extrêmement dynamique . . . . . . . . . . . . . . . . 117

Le compteur interactif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

Diagramme de classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

Première version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

Deuxième version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

Troisième version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

Quatrième version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

Les classes internes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

Exercice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Chapitre 9 JavaScript, Java: quels rapports? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

Un dilemme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

Un air de famille? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

Les langages de script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133

JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

Java et le HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

Un brin de comparaison . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Un autre exemple en JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

Saisie de nombres et calcul d’une moyenne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

Bibliographie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141

Page 7: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

1 Dans certains cas, un raisonnement déterministe n’est pas toujours celui qui est le plusapproprié.

Chapitre 1 Pourquoi programmer avec des objets? - 1 -

Chapitre 1 Pourquoi programmer avec des objets?

Introduction

Il n’entre pas dans mes intentions de débattre ici des différents paradigmes de programmation et deleurs intérêts respectifs. De nombreux ouvrages et articles traitent de cette problématique. L’existencede ces paradigmes et les succès relatifs des langages qui s’en inspirent prouvent que les domainesd’application de l’informatique sont suffisamment étendus pour laisser de nombreux champsd’application possibles à chacun d’eux.

Je signalerai simplement que la naissance d’un nouveau paradigme vient évidemment des limitesatteintes par les langages qui sont associés aux paradigmes courants. La programmation impérative abien répondu aux attentes, dès lors que les applications développées ne comptaient guère plus dequelques milliers de lignes et étaient le produit d’un ou deux programmeurs.

Plusieurs pistes ont été et sont toujours explorées parmi lesquelles, celle de la programmation logiquechère à tous ceux qui s’intéressent à l’intelligence artificielle et aux domaines dans lesquels la quantitéde données à traiter est gigantesque. Mais il y a d’autres aspects. Ainsi, l’augmentation phénoménalede la complexité des logiciels fait qu’aujourd’hui, le nombre de lignes de code s’élève très souvent àdes dizaines de milliers. Ce sont des équipes de programmeurs qui travaillent à la réalisation de ceslogiciels et il faut éviter des pertes de temps dues à d’éventuelles synchronisation du travail. Chacundoit pouvoir travailler dans son coin en faisant confiance aux autres et en se montrant digne de laconfiance des autres.

En conséquence, des impératifs nouveaux apparaissent. Il devient nécessaire:

• de penser les traitements d’une manière plus naturelle, plus proche de la réalité1 afin de dégager leprogrammeur de tout une série de contraintes opérationnelles;

• d’encapsuler les traitements dans des modules qui soient des boîtes noires équipées d’une interface,permettant au programmeur de les utiliser sans savoir comment ces traitements sont implantés, maisconnaissant les éventuelles données à fournir et les éventuels résultats retournés;

• d’évoluer ainsi vers un développement qui favorise la réutilisation logicielle et l’adaptabilité.

C’est essentiellement de ces nécessités qu’est née la programmation orientée objet. Cela expliquenotamment que ce n’est pas un programme d’addition de nombres saisis au clavier jusqu’à ce qu’unsignal soit donné (valeur spéciale ou bidon) qui vous convaincra du bien-fondé de ce type deprogrammation et cela, même si un tel programme peut être écrit au moyen d’objets.

Page 8: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

2 Une entreprise se moque pas mal de faire réécrire des programmes qui fonctionnent bien dansde nouveaux langages, dans la mesure où la réécriture est une source de problèmes potentiels.

3 La Programmation Orientée Objet (POO) se caractérise par l’utilisation d’un certain nombre demécanismes liés à la création des objets. La plupart des langages de programmation serevendiquant de cette catégorie ne peuvent cependant gommer totalement tous les aspects dela programmation impérative. C’est en ce sens qu’on parle de programmation orientée objet. Certains langages permettent de les réduire à la portion congrue en exploitant au maximum lesmécanismes de la POO, d’autres continuent à nécessiter une part importante de programmationimpérative tout en proposant certains des mécanismes en question. On parlera donc delangages complètement orientés objet pour désigner les premiers et de langages orientés objetpour désigner les autres et cela, quel que soit leur degré de respect des mécanismes de la POO.

4 C’est en tous cas ma volonté.

Chapitre 1 Pourquoi programmer avec des objets? - 2 -

Quant à la programmation impérative, elle a encore de beaux jours devant elle, ne fusse que par lanécessité de maintenir une quantité de programmes utiles et qui fonctionnent2. Le but de ce cours n’estpas de vous convaincre que la programmation orientée objet3 est la solution à tous les problèmes, maisseulement de vous faire percevoir quels en sont les mécanismes fondateurs afin de vous permettre dejuger, dans quelles situations celle-ci se révèle intéressante.

Toutes ces choses étant dites, il faudra sans doute perdre un peu de vos habitudes de programmationpour bien cerner tout le profit que vous pourrez tirer de cette autre manière de programmer.L’introduction qui sera faite et les exemples qui seront choisis tenteront de vous faire oublier vosanciennes amours et de vous attirer hors de leur champ d’influence.

Ce cours a aussi pour objectif d’analyser dans quelle mesure les concepts qui y sont développés sontaccessibles à des élèves du degré supérieur de l’enseignement secondaire. Vos avis seront donc trèsautorisés en la matière. Les notions seront présentées de la manière la plus pédagogique possible4 demanière à pouvoir en juger efficacement.

Le choix d’un langage

Une dernière chose enfin, il n’est pas possible de parler langage de programmation sans en choisir unen particulier. Ceux qui ont suivi des cours sur la programmation impérative ont souvent utilisé lelangage Pascal pour développer des applications. Beaucoup d’experts du domaine de l’enseignementestimaient, sans doute à raison, que ce langage était pédagogique, même s’il n’offrait pas les facilitésde développement de certains autres langages. Nous utiliserons principalement le langage Java, mêmesi je caresse l’espoir de pouvoir faire une petite incursion du côté d’un langage de script (enl’occurrence, Javascript). En voici, de mon point de vue et par ordre décroissant d’importance à mesyeux, les principales raisons:

• Java est un langage de programmation qui met en oeuvre assez clairement les mécanismesfondamentaux de la programmation orientée objet que sont l’héritage, la surcharge et lepolymorphisme;

Page 9: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

5 Il existe une autre solution: installer Jbuilder 5 Personal un produit de Borland qui offred’excellentes possibilités d’édition de programmes en Java. Ce logiciel contient une versiondu JDK 1.3 (Java Development Kit). Cette version est gratuite pour autant que vousrespectiez le contrat de licence qui vous est proposé. Elle est téléchargeable à partir del’adresse http://www.borland.com/jbuilder/. Le fichier compressé à télécharger est de 40 Mopour la version Windows et de 53 Mo pour la version Linux.

Bien entendu, il est tout à fait possible de rédiger du code Java en utilisant le simple bloc-notes de Windows ou un éditeur de texte élémentaire. Un produit comme Jbuilder fournit auxdéveloppeurs des tas de services intéressants allant de la coloration syntaxique à la générationde documentation de programme en passant par l’indentation automatique, une gestion soupledes fichiers etc. La relative convivialité du produit parle en la faveur de son auto-découvertecar soyons clairs, la connaissance d’un tel outil dédié aux développeurs professionnels n’estpas l’objectif du cours. Son étude nous éloignerait d’ailleurs de l’essentiel.A vous de voir si l’installation de Jbuilder, à terme, vous sera utile. Tenez compte égalementdes performances de votre matériel.

Chapitre 1 Pourquoi programmer avec des objets? - 3 -

• Java demande de la rigueur dans la structure et l’écriture des programmes et les implicites y sontmoins nombreux que chez la plupart des langages de script (Javascript, Perl, Php, Python,...);

• Java a grandi avec le Web, ce qui le rend bien adapté à son exploitation;

• un programme écrit en Java et compilé en octets de code peut tourner sur n’importe quel type dematériel et n’importe quel type de plateforme.

Finalement, les huit cours qui sont prévus s’avèreront insuffisants pour couvrir un aperçu assez largede ce que propose Java. Sans connaître ni les capacités du groupe, ni le temps qu’il me faudra pourmettre en place les fondements du langage, j’opterai pour l’installation solide des concepts, même sice choix m’oblige à envisager avec vous une suite au cours de l’année académique prochaine.

C’est une évidence mais laissez-moi vous redire que la bonne compréhension d’un tel cours passe parune pratique minimum. Entre les différentes séances, je ne puis que vous encourager à programmer lespetites applications que je vous proposerai.

L’installation

Cette section est un peu plus technique mais il faut bien décrire quelque part, comment acquérir uneversion du langage Java et comment l’installer.

Je vous suggère d’installer Java2 SDK (Software Development Kit) version 1.3. Cette version estactuellement appelée Java2 Platform Standard Edition v. 1.3. Vous le trouverez à la source c’est-à-dire à l’adresse http://java.sun.com/j2se/1.3/ et cela, que votre OS préféré soit Windows, Linux oumême Solaris. Un exécutable d’environ 40 Mo est à télécharger. Son exécution installe le langage ettoute la hiérarchie des librairies dont il a besoin. Sous Windows, le dossier par défaut estc:\jdk1.3.1_02 mais il est possible de faire un autre choix de dossier5.

Page 10: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

6 Cette opportunité nous sera de peu d’utilité tant que nous ne développons pas desapplications qui interagissent entre elles.

Chapitre 1 Pourquoi programmer avec des objets? - 4 -

La machine Java virtuelle (JVM)

La description qui suit n’est pas nécessaire à la compréhension du prochain chapitre. Sa lecture peutêtre différée. Cependant, on évoque tellement souvent la machine virtuelle qu’il me semble utile d’endonner une explication à ce stade à l’intention de ceux qui connaissent déjà un peu les tenants etaboutissants de la programmation orientée objet.

Une des préoccupations qui se fait jour au moment du développement du projet Java, c’est laportabilité des applications sur différentes plate-formes. C’est une exigence qui naît du développementd’Internet mais aussi de l’évolution de l’architecture informatique de nombreuses grosses entreprises,de systèmes centralisés vers des systèmes distribués. La variété du matériel et des systèmesd’exploitation en interconnexion impose que les applications développées dans des langages modernespuissent être hébergées sur des systèmes très hétéroclites.

Comment cette caractéristique de portabilité est-elle obtenue? Que représente cette machine virtuellequ’on évoque pour expliquer cette qualité du langage. En voici une très brève explication.

L’installation de Java dont il vient d’être question consiste notamment en la fourniture :

• d’un grand nombre de classes prédéfinies

• de plusieurs outils indispensables (compilateur, interpréteur, générateur de documentation,débogueur,...)

Pourquoi un compilateur et un interpréteur? Le principe est le suivant: sur un système donné, lecompilateur transforme le code source (texte des programmes) en “octets de code” (bytecodes enanglais). Il s’agit d’un langage universel que chacun des systèmes s’engage à déchiffrer au moyen deson interpréteur spécifique capable de digérer ces octets de code en tenant compte du contexte local.Les bytecodes générés par la machine A gouvernée par l’OS A’ peuvent maintenant être interprétéspar l’interpréteur de la machine B gouvernée par l’OS B’.

En d’autres termes, un même programme, compilé par différents systèmes, fournit les mêmesbytecodes. A l’exécution du programme sur un système donné, ces bytecodes sont interprétés grâceà un interpréteur propre au système. Le couple constitué de ce système donné et d’une instance del’interpréteur est appelé machine virtuelle. Un même système peut donc être à la base de l’existencede plusieurs machines virtuelles6.

Nous verrons par la suite que pour qu’un programme puisse être exécuté, il faut qu’existe une classeexécutable. Elle doit être enregistrée dans un fichier qui porte le même nom qu’elle suivi de l’extensionjava.

Si la classe exécutable s’appelle Application, elle doit être enregistrée dans un fichier qui s’appelle Application.java.

Page 11: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

7 Il est clair que la définition d’une classe, y compris celle d’une classe exécutable, faitgénéralement appel à d’autres définitions de classes contenues dans d’autres fichiers. Lacompilation consiste à générer des bytecodes qui intègrent les informations contenues dansces différents fichiers.

8 Cette commande peut comporter d’autres paramètres. En particulier, nous verrons qu’il estpossible, et même souhaitable, de définir dans les différentes commandes les chemins d’accèsaux fichiers. Pour ce qui est du lancement d’une JVM, on peut aussi fournir des paramètresqui sont récupérables par le programme.

Chapitre 1 Pourquoi programmer avec des objets? - 5 -

Le code contenu dans ce fichier (et dans les fichiers qui lui sont liés7) est compilé grâce à la commandejavac suivie du nom du fichier. La commande javac correspond donc à l’activation de l’outil decompilation. Le fichier résultant portera le nom de la classe suivi cette fois de l’extension class.

La compilation du fichier Application.java se fait par la commande

javac Application.java

et produit un fichier Application.class.

Le code de cette classe est exécuté en utilisant la commande java suivi du nom de ce dernier fichier,sans l’extension. Elle correspond à l’activation de l’interpréteur de bytecodes.

La commande

java Application

produit l’exécution du programme8.

Toutes les commandes qui viennent d’être évoquées sont données au niveau d’un shell de commandesdu système d’exploitation utilisé et fonctionnent sous réserve d’un choix correct des paramètresconcernant les variables d’environnement (notamment les chemins que l’OS doit suivre pour trouverles programmes et ceux que les programmes doivent suivre pour trouver le code des applications).

Page 12: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

9 Il faudra bien qu’un objet engage la conversation!

Chapitre 2 Des objets qui dialoguent... - 6 -

Chapitre 2 Des objets qui dialoguent...

Une approche naturelle

Un des reproches que l’on peut faire à la programmation impérative est d’éloigner le programmeur dumonde réel en l’obligeant à décrire, d’une manière très élémentaire, des actions et des traitements quisont parfois très complexes. Les limites de ce type de démarche sont atteintes dès l’instant oùl’ensemble de ces instructions devient pléthorique. Il devient alors difficile de contrôler l’ensemble desmodules d’une application, même lorsque l’analyse de celle-ci a fait l’objet d’une approchedescendante. Une autre critique importante revient régulièrement lorsqu’on parle de programmationimpérative: l’application programmée répond généralement à une vision, espérons-le correcte, que l’ona des traitements au moment du développement de celle-ci. La conséquence, c’est que lesprogrammes rédigés manquent souvent d’adaptabilité et que le monde est (trop) souvent à refaire.

La programmation orientée objet trouve une partie de son intérêt dans la réponse qu’elle apporte à ladifficulté qui vient d’être évoquée. Pour prendre un exemple simple, un patron ne peut gérer une trèsgrosse entreprise en voulant s’occuper des moindres détails de son fonctionnement, des grandesdécisions stratégiques, jusqu’à l’achat des produits d’entretien des locaux, en passant par la paie desouvriers, par exemple. Il doit pouvoir faire confiance à ses subordonnés et se limiter à une interactionavec eux. C’est un peu l’idée de la programmation orientée objet d’arriver à constituer des logicielsbasés sur l’interaction entre les objets, chacun pouvant demander à d’autres d’effectuer certaines tâchespour la réalisation desquelles eux-mêmes s’adresseront à d’autres objets et ainsi de suite. Un desprincipes sur lesquels nous reviendrons est basé sur le fait qu’une relation de confiance existe entre lesobjets. Chacun d’entre eux propose et s’engage à rendre au monde extérieur (les autres objetspotentiels) un ensemble de services. Il le fait via une interface d’interaction qui ne permet pas de savoircomment ces services sont rendus.

Le déroulement d’une application ressemblera à une conversation entre objets9. Ceux-ci vont doncs’envoyer mutuellement des messages qui seront autant de demandes de sous-traitance. Si un objetne peut réaliser une partie du travail qui lui est demandé, il devra pouvoir confier cette partie du travailà un autre objet. La conversation entre objets sera donc aussi constituée de réponses que les objetss’enverront les uns les autres. Pour établir une comparaison avec des choses connues, une demandede service d’un objet à un autre est une sorte d’appel de fonction. Dans le vocabulaire de la POO, onparlera d’appel de méthodes.

L’encapsulation

Page 13: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

10 Dans le contexte d’une bonne programmation, les classes sont généralement biendocumentées, qu’il s’agisse de décrire le contrat rempli par chacune des méthodes ou dedétailler la nature des autres membres de la classe.

Chapitre 2 Des objets qui dialoguent... - 7 -

C’est un des principes essentiels de la POO. Un objet est caractérisé par son comportement et parson état. Son comportement est décrit par les services qu’il peut rendre et donc, d’une certainemanière par les fonctions qu’il peut remplir. Nous avons déjà signalé qu’on parlait ici de méthodesplutôt que de fonctions. L’état d’un objet est ce qui le caractérise à un moment donné. Cet état estvariable, dépend de ses comportements et des comportements des autres objets. La structure d’unobjet ne lui est généralement pas propre. Il fait partie d’une classe d’objets qui la partagent. Il en estainsi dans le monde réel. Une personne, une voiture, un chien, une carte d’identité,... possèdentgénéralement une série de propriétés communes, même si les valeurs qui y sont attachées sontdifférentes. Les personnes n’ont pas le même nom, les chiens ne sont pas de la même race, les cartesd’identité ne contiennent pas la même photo,... Ils ont également une série de comportementsidentiques. Les personnes parlent, les chiens aboient, les voitures peuvent freiner et accélérer,...

Il n’est pas du tout souhaitable que chaque objet puisse modifier anarchiquement etdirectement l’état des autres objets. Il est préférable de réserver la modification de l’état à l’objetlui-même. Ceci n’implique pas qu’un objet puisse demander cette modification mais il devra ledemander à l’objet lui-même qui pourra, notamment, s’assurer que cette modification est valide. Il vautmieux, en effet, qu’un seul objet prenne cette responsabilité en charge. Ces remarques vont nousconduire à définir, plus tard, la portée des variables et des méthodes.

Si le comportement d’un objet est caractérisé par les méthodes qu’il supporte, son état estprincipalement décrit par des variables propres appelées variables d’instances. Pour désigner cetteassociation de l’état d’un objet à son comportement, on parle d’encapsulation. Les données et lestraitements ne semblent plus séparés mais donnent l’impression d’être regroupés à l’intérieur de l’objetlui-même. L’encapsulation présente un avantage incontestable. Lorsqu’un programmeur utilise desclasses qu’il n’a pas développées lui-même et en crée des instances, il ne connaît pas la manière dontles traitements correspondant à l’exécution des méthodes sont implantés. La seule chose que celui-ciconnaisse, c’est l’ensemble des services rendus par la classe, le contrat qu’elle s’engage à remplir enquelque sorte10. Cet avantage n’est pas à négliger lorsqu’il s’agit d’évoluer vers un type deprogrammation dont une partie importante de la démarche consiste à réutiliser des modules (logiciels)existants.

Un exemple simple

Considérons un carré.

On peut imaginer que son état soit décrit par deux données variables que sont la longueur de son côtéet la coordonnée dans un repère défini de son coin supérieur gauche .

Le fait d’évoquer un carré contient implicitement la référence à un objet de la classe des carrés. Onpeut demander beaucoup de services à un objet de type carré. Les plus évidents sont sans doute:

• calculer et fournir son périmètre,

Page 14: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

11 Dans une perspective graphique plus poussée, on pourrait considérer que le carré possède unfond coloré, un bord ayant lui-même une épaisseur et une couleur, etc.

12 De par le fait qu’un objet encapsule ses données et ses méthodes, on assimile parfois le mot“objet” au mot “logiciel”. Certains objets peuvent en effet être d’une grande complexité.

13 Dans ce cas-ci, ça peut paraître évident. Notez toutefois que le calcul pourrait consister à fairemultiplier l’aire du carré par 16 et prendre la racine carrée du résultat. La seule chose quiintéresse l’utilisateur (programmeur) c’est la garantie d’obtenir la valeur du périmètre.

Chapitre 2 Des objets qui dialoguent... - 8 -

• calculer et fournir son aire,

• se dessiner,

• changer ses propres dimensions ou sa position,

• fournir certains de ces renseignements à la demande,

• ...11

Lorsqu’on demandera à un objet carré de changer la valeur de la longueur de son côté, celui-ci pourravérifier que la valeur fournie est cohérente et faire en sorte de corriger les éventuelles erreurs pour queles données soient consistantes. Par exemple, si le paramètre fourni pour la longueur du côté estnégatif, l’objet pourra décider de lui donner la valeur 0. Le fait de confier cette tâche à l’objet lui-mêmecombiné au fait que l’accès aux données peut lui être réservé, empêche tout objet (logiciel12) extérieurde rendre le système de données inconsistant.

Les classes d’objets et les mécanismes de la POO

En programmation orientée objet, chaque objet fait partie d’une classe dont la description mentionneles données et le comportement qui le caractérisent ainsi que ses semblables. En suivant l’exemple, ladescription de la classe des carrés mentionnera l’existence de deux champs de données: la longueurdu côté et la position du sommet supérieur gauche. Elle précisera les méthodes disponibles, celle quicalcule et affiche le périmètre, celle qui modifie la position du sommet,... en indiquant chaque fois lenombre et le type des paramètres à fournir.

Un programmeur qui utilisera la classe carré saura qu’il peut en obtenir le périmètre sans savoircomment le calcul est effectué13.

Le mot “type” est bien connu des programmeurs. La programmation orientée objet fera généralementapparaître deux sortes de types: les types primitifs (entiers, caractères, booléens,...) et les typesconstruits (ceux qui correspondent aux classes d’objets). Ainsi, un objet peut être déclaré de typecarré, cela signifiera qu’il est caractérisé par tel et tel types de données et par telle et telle méthodes.

Notre exemple peut constituer une application directe de ce qui précède. Nous pouvons décider quele type de donnée pour la longueur du côté est “nombre entier” qui est un type primitif et que le typede donnée pour le sommet est point ou point constitue une autre classe d’objets.

Page 15: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

14 Ceci a (peut-être) de quoi faire dresser les cheveux sur la tête des mathématiciens. Dire qu’uncarré est une sorte de point est en effet assez osé. Le mathématicien dirait plutôt qu’un pointcorrespond à un carré dont la longueur du côté est nulle... et encore. Pourtant, cette manièrede voir les choses peut être commode en POO.

15 Attention, l’inverse n’est pas vrai car la classe qui hérite “spécialise” la classe d’héritage. Ilest fort probable, par exemple, qu’elle supporte des méthodes que la précédente ignore.

Chapitre 2 Des objets qui dialoguent... - 9 -

Toutes ces possibilités prendront forme dans le chapitre suivant consacré à Java. En attendant, et pourmontrer que la programmation orientée objet fait la part belle à l’abstraction, nous pourrions encoreavoir une autre vue des choses et décider de créer d’abord une classe point qui contiendrait, parexemple, deux données de type nombre entier (une abscisse et une ordonnée). Nous choisirions alorsde dériver la classe carré de la classe point en précisant qu’un carré, c’est d’abord un point (sonsommet supérieur gauche) avec en plus, la longueur d’un côté (ce qui permettrait de le définircomplètement)14. Ceci permet d’amorcer la présentation d’un des mécanismes classiques de la POOà savoir l’héritage.

L’héritage est ce que l’on a inventé de mieux pour justement ne pas devoir tout réinventer. En sebasant sur le principe qui veut que ce qui a déjà été défini peut être réutilisé ou légèrement modifié etque le reste peut être ajouté, une classe d’objets peut hériter d’une autre classe en la spécialisant. Cettespécialisation consiste:

• à rajouter des choses qui n’existaient pas dans la définition de la classe précédente, à savoir deschamps de données ou des méthodes;

• à redéfinir certaines méthodes en leur faisant faire les choses de manière différente, tout en seménageant la possibilité de considérer l’objet comme faisant aussi partie de la classe d’héritage.

On peut aussi envisager les choses d’une autre manière en considérant que des objets appartenant àdes classes différentes ont des points communs qui pourraient permettre de les considérer commefaisant tous partie d’une classe d’objets plus générale. L’héritage s’examinera donc souvent dans lesens de la spécialisation mais parfois aussi dans celui de la généralisation.

Cette petite réflexion nous permet aussi d’amorcer la description d’un autre mécanisme fondamental:le polymorphisme .

Si la classe d’un objet hérite d’une autre classe, tout objet de la première est aussi un objet de laseconde15. De ce fait, l’objet a plusieurs formes possibles et on peut donc l’invoquer en le considérantde différentes manières. C’est pourquoi on parle de polymorphisme.

Il existe en POO d’autres mécanismes tels, par exemple, la surcharge. Un résultat peut être renvoyépar une méthode sur base de données différentes. Ainsi, le calcul de l’aire du carré peut être réalisésur base de la connaissance de la longueur du côté de celui-ci, mais aussi, pourquoi pas, sur base dela connaissance des coordonnées de deux sommets opposés. Dans le premier cas, l’unique paramètreest un simple entier alors que dans le second, les deux paramètres sont des objets “points”. Pour qu’uncompilateur puisse identifier une méthode sans équivoque, il se réfère à sa signature qui comprendnécessairement le nom de la méthode mais aussi la liste (éventuellement vide) des paramètres et de leurs

Page 16: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

16 Dans notre exemple, le résultat renvoyé est un entier qui représente l’aire du carré mais rienn’empêche que la sémantique même des résultats soient tout à fait différentes selon le nombreet le type des paramètres de la méthode.

17 Le langage Java, que nous étudierons au prochain chapitre, n’implante pas directement lanotion d’héritage multiple. Une classe ne peut hériter que d’une seule autre classe. Néanmoins, le concept d’interface autorise sa simulation en en supprimant les désavantages.

18 Pardonnez-moi d’avoir puisé cet exemple dans le domaine des mathématiques, ma premièrevocation ;-)

Chapitre 2 Des objets qui dialoguent... - 10 -

types. C’est en ce sens qu’on parle de surcharge de la méthode. On peut donc dire qu’un même nomde méthode dissimule des sémantiques différentes16.

Nous avons évoqué le mécanisme d’héritage. Beaucoup de langages de POO implantent unmécanisme d’héritage multiple. Une classe peut hériter de plusieurs autres classes au sens où nousl’avons défini. La sous-classe hérite alors de caractéristiques de toutes les classes dont elle hérite. Celane va pas sans poser un certain nombre de problèmes17.

Cette première approche devrait suffire à comprendre dans quel esprit fonctionne la POO.L’algorithmique reste bien présente, notamment au niveau de la description des méthodes. Néanmoins,tout code préalablement rédigé et qui fonctionne, c’est-à-dire qui remplit son contrat, ne doit plus êtrepris en compte par celui qui utilise l’objet. Cette approche autorise, vous l’aurez compris, un meilleurdébogage et une meilleure localisation des problèmes.

Dans le chapitre suivant, nous vous proposons de découvrir comment Java, un langage qualifié detotalement orienté objet, implante les concepts qui viennent d’être décrits. Ce sera aussi l’occasionde découvrir la syntaxe de ce langage et de souligner quelques bonnes habitudes rédactionnelles. Ala fin du syllabus, nous tentons une comparaison, si tant est qu’elle ait du sens, entre Java et JavaScriptque l’on pourrait qualifier de faiblement orienté objet.

Exercice unique

En vous basant sur l’exemple du carré dont les sommets sont des points qui ont chacun unecoordonnée, identifiez, dans les domaines que vous connaissez, des objets dont la définition faitéventuellement appel à d’autres objets. Imaginez, pour ces objets et ceux qui les composent, desméthodes que l’on pourrait invoquer auprès d’eux en précisant ce que ces méthodes sont senséeseffectuer comme traitement ou renvoyer comme résultat.

Pour que les choses soient claires, voici d’autres orientations possibles pour étoffer l’exemple du carréet des points18. Elles seront traitées dans les chapitres qui suivent. Vous devriez pouvoir tenir undiscours comme celui qui suit, discours qui vous permettrait de dégager les classes, les champs de

Page 17: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

19 Il serait utile que chacun s’impose une telle description de manière à pouvoir la faire partagerlors de la prochaine séance. Cette réflexion pourrait conduire à la constitution d’une mini-basede données d’exemples exploitables en situation d’enseignement.

Chapitre 2 Des objets qui dialoguent... - 11 -

données et leurs types (donc éventuellement d’autres objets) ainsi que quelques méthodes supportéespar ces classes d’objets19.

Une droite est constituée de points. Deux points permettent de définir une droite, mais il y peuty avoir d’autres moyens. On peut demander à une droite de changer de direction, de glisserparallèlement à elle-même. Pour autant que l’on ait défini ce qu’est un cercle, on peut demanderà une droite si elle est tangente à un cercle donné, si elle comprend un point donné etc.

De la description qui précède, on peut retenir qu’à tout le moins, il existe des points, des droites et descercles. Pour ce qui est des droites, on peut imaginer une méthode qui répondra par vrai ou faux à laquestion: comprends-tu tel point? Il s’agira d’un résultat. Une autre méthode ne fournira pas derésultat mais modifiera les paramètres de la droite lorsqu’on lui demandera de glisser parallèlement àelle-même pour passer par un point donné etc.

Page 18: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

20 Je me permets d’en rajouter encore un peu à l’avertissement, en vous faisant remarquer que lesobjets dont il est ici question ne s’inscrivent pas dans une problématique bien précise. Il fautd’ailleurs reconnaître qu’un des intérêts de la POO, c’est de pouvoir revisiter la définition desobjets en fonction de nécessités nouvelles. On définira donc souvent des classes d’objets demanière très schématique, voire très courte, simplement parce qu’on les a identifiées enréservant à plus tard une description plus détaillée. Le fait que certaines classes d’objets nesoient pas utilisées “in fine” n’est pas vraiment un problème.

Chapitre 3 Java: notions de base - 12 -

Chapitre 3 Java: Notions de base

Avertissement

Dans ce chapitre, j’éviterai, autant que possible, de vous noyer dans une multitude de détails, mêmesi ceux-ci sont importants. Je me contenterai de préciser les éléments qui vous permettront assezrapidement de réaliser une petite application Java en mettant d’emblée l’accent sur les concepts quime paraissent fondamentaux. A ce stade, il n’est pas encore question de mettre en oeuvre lesmécanismes principaux de la POO.

Nous sommes évidemment encore bien loin de pouvoir nous intéresser à la manière de conduire ledéveloppement d’une application. . Il y a tellement de choses à illustrer que nous laisserons un peu decôté (pour l’instant) la méthodologie.

Définition d’une classe

Pour une introduction en douceur de la syntaxe Java, intéressons-nous aux classes que nous avonsévoquées dans le chapitre précédent20. Définissons-les en apportant à leur définition les commentairesutiles.

La classe Point

Pour rappel, cette classe caractérise des objets Point qui ont une coordonnée composée d’uneabscisse et d’une ordonnée. Nous décidons que ces deux informations sont des nombres entiers.Voici à quoi pourrait ressembler la définition de cette classe.

class Point{

public int x; public int y;

}

Page 19: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

21 Java utilise la norme IEEE 754-1985.

22 Attention, syntaxiquement ‘A’ désigne le caractère A alors que “A” désigne la chaîne decaractères A. Il n’y a pas de type primitif pour les chaînes de caractères mais il existe uneclasse String prédéfinie.

Chapitre 3 Java: notions de base - 13 -

Dans la définition d’une classe, le mot-clé class est suivi du nom de la classe. Le nom d’une classe doit impérativement commencerpar une lettre et peut se poursuivre par des chiffres et des lettres. En Java, on utilise conventionnellement des noms de classescommençant par une lettre majuscule et on utilise des lettres majuscules au début de chaque autre mot dans le nom: Formes,FormesGeometriques, CompteurDeMots,... Attention, Java est sensible à la casse.

Tout ce que la classe englobe en termes de champs de données et de méthodes est encadré par des accolades. Une bonnehabitude de rédaction consiste à faire suivre le nom de la classe de l’accolade ouvrante et de placer l’accolade fermante au mêmeniveau que le premier caractère de la ligne de définition de la classe, ce qui permet une détection rapide des erreurs de syntaxe duesà leur absence.

Comme dans de nombreux langages, le séparateur d’instruction en Java est le point-virgule.

Le mot public utilisé pour les champs de données, mais qui peut l’être aussi pour la classe et les méthodes, est un modificateurd’accès. Nous discuterons plus tard de l’opportunité d’élargir ou de restreindre les accès aux champs de données et aux méthodes.

Les objets de cette classe comptent deux champs de données qui sont du type primitif “entier” (int) et ne supportent, pour l’instant,aucune méthode.

Les types primitifs

Java est un langage fortement typé. Le type de chacune des variables doit être impérativement précisé.Un rapide premier contact avec ces types primitifs nous apprend qu’ils sont au nombre de huit:

• quatre types entiers;

• deux types réels à virgule flottante;

• un type caractère;

• un type booléen.

Le type int est codé sur 4 octets, le type short sur 2 octets, le type long sur 8 octets et le type bytesur un seul.

Le type float est codé sur 4 octets et le type double sur 8 octets21.

Le type char correspond à l’Unicode (2 octets)22.

Le type boolean prend les valeurs true et false.

Les méthodes

Nous allons pallier l’absence de méthodes de la classe point en créant une méthode qui fournit la valeurde l’abscisse du point, une qui fournit son ordonnée et deux autres méthodes qui permettent de lesmodifier. Ces méthodes sont donc autant de services que l’objet peut rendre au monde extérieur.

Page 20: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

23 Lorsque le paramètre est un objet, le type n’est pas un type primitif, mais la classe à laquellel’objet est censé appartenir.

24 Il est bon de donner aux champs d’une classe l’accès le plus restreint possible et à l’élargir enfonction des nécessités. Dans le tout premier exemple, la classe Point ne comprenait pas deméthode. Il était donc impératif que les champs soient déclarés public.

Chapitre 3 Java: notions de base - 14 -

class Point{

private int x; private int y;

public int getAbscisse(){ return x;

}

public int getOrdonnee(){ return y;

}

public void setAbscisse(int a){ x=a;

}

public void setOrdonnee(int b){ y=b;

}

}

La définition d’une méthode commence généralement par un modificateur d’accès.

Elle comprend également le type de résultat renvoyé par la méthode s’il y en a un. Les méthodes getAbscisse et getOrdonneerenvoient un résultat dont le type est le type primitif “entier”. En cas d’absence de résultat retourné, le mot clé est void comme dansles méthodes setAbscisse et setOrdonnee.

Le choix du nom de la méthode est soumis à la recommandation suivante: lettres minuscules (et éventuellement chiffres) et majusculeà la première lettre de chaque mot à partir du deuxième.

Le nom de la méthode est suivi des paramètres éventuels précédés de leur type et séparés par des virgules23.

L’opérateur d’affectation est symbolisé par le signe =. Lorsqu’il s’agit d’affectation entre objets, ce sont des références qui sont unifiées.Voyez à ce propos ce qui est dit de la construction des objets.

Le choix d’un accès public pour les méthodes est assez classique. Les objets extérieurs doiventpouvoir invoquer la méthode de l’objet sauf si celle-ci est destinée à l’usage exclusif de la classe. Dansce cas, il vaut mieux la déclarer private.

L’accès aux champs de données est maintenant private24. C’est une bonne idée que d’empêcher lamodification de l’état de l’objet par d’autres objets que lui-même. En d’autres termes, plutôt que de

Page 21: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

25 La notion d’interface est la réponse de Java à l’absence de mécanisme d’héritage multiple. Cette notion est développée plus loin.

26 A titre d’exception, nous verrons qu’il existe des classes qui ne sont jamais instanciées.

27 La signature d’une méthode ou d’un constructeur comprend son nom, la liste des paramètreset leur type.

Chapitre 3 Java: notions de base - 15 -

modifier l’état d’un autre objet, un objet invoquera la méthode de ce dernier pour accomplir le travail.L’intégrité des données est ainsi plus facile à assurer de même que le débogage et la maintenance. Onsent que l’idée sous-jacente est de créer du logiciel qui soit facile à adapter, grâce à une localisationprécise des endroits où les traitements sont effectués.

Les membres de classes

Les méthodes et les champs de données sont appelés membres de classe. Une classe peut aussiposséder, comme membres, d’autres classes (ou des interfaces25) mais cela ne nous est pas très utilepour l’instant. Le type d’accès à chaque membre de classe doit être spécifié. Jusqu’à présent, nousavons évoqués les accès public et private mais l’accès protected pourra aussi nous intéresser par lasuite.

Les constructeurs

Un programme OO a pour objectif essentiel de faire naître des objets dans le but de les fairecommuniquer et réagir. Pour prendre un exemple très élémentaire, lorsqu’un objet carré est créé, unautre objet peut en invoquer les méthodes afin d’obtenir des informations à son sujet (son aire, sonpérimètre,...). Cela signifie qu’il est nécessaire de disposer d’une technique de construction des objets.A cette fin, chaque classe doit contenir au moins un constructeur d’objets26. Le constructeur d’objetsest une sorte de méthode de fabrication de l’objet bien qu’il ne s’agisse pas d’une méthode au sensdéfini précédemment. Il existe un constructeur par défaut qui ne comporte évidemment aucunparamètre. Le programmeur a le loisir de définir autant de constructeurs qu’il le souhaite. Ceux-ci vontdifférer par les paramètres qu’ils vont accepter et par les initialisations des objets qu’ils vont effectuer.

Nous savions déjà que la définition d’une classe se composait de la définition des champs de donnéeset de celle des méthodes de la classe. Nous pouvons maintenant y ajouter les constructeurs des objets(autres que le constructeur par défaut qui ne doit pas être défini).

Un constructeur d’objets d’une classe porte toujours le nom de la classe. Sa signature27 peutcomporter de 0 à n paramètres.

Comment, concrètement, un objet est-il créé? Le jeu de la communication des objets entre eux faitqu’à un moment donné du déroulement du programme, une méthode d’une des classes d’objets est entrain de s’exécuter. Il est possible que cette exécution résulte elle-même d’un appel provoqué parl’exécution d’une autre méthode associée à un autre objet ou à une autre classe. La création d’un objetest une des instructions possibles faisant partie du code de la méthode exécutée. La manière dontl’objet est créé (l’instruction) détermine celui des constructeurs qui doit être choisi.

Page 22: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 16 -

Améliorons la classe Point en y ajoutant deux constructeurs.

public class Point{

private int x; private int y;

public Point(){ setPoint(0,0);

}

public Point(int a,int b){ setPoint(a,b);

}

public int getAbscisse(){ return x;

}

public int getOrdonnee(){ return y;

}

public void setAbscisse(int a){ x=a;

}

public void setOrdonnee(int b){ y=b;

}

public void setPoint(int a,int b){ x=a; y=b;

}

}

Le premier constructeur est sans paramètre. Son utilisation provoque la création d’un point origine.Le second constructeur demande deux paramètres de type entier.

Syntaxiquement, la création d’un point prendra une de ces formes:

Point p1=new Point();

Point p2=new Point(3,5);

ou une forme équivalente comme

Point p1;

int a=4;

...

Page 23: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 17 -

p1=new Point(4,a)

Une bonne habitude consiste à choisir comme identificateurs de variables des chaînes de caractères contenant des lettres etéventuellement des chiffres en utilisant la même règle que pour les méthodes (lettres minuscules et première lettre en majuscule à partirdu deuxième mot; exemple: centreDuCercle).

Notez aussi que les types primitifs commencent par une minuscule ce qui permet de les distinguer des types correspondant auxclasses d’objets.

Dans la description de la classe, nous avons rajouté une méthode setPoint qui permet de modifier àla fois l’abscisse et l’ordonnée d’un point. Cette méthode apparaît dans la définition des deuxconstructeurs. Les constructeurs sont déclarés d’accès public ce qui permet à des méthodes d’autresclasses d’invoquer la création de nouveaux objets.

Chaque fois qu’un objet est créé, on dit que la classe est instanciée. Les variables que constituent leschamps de données d’un objet sont propres à chacun d’eux, malgré qu’elles portent les mêmes noms.C’est la raison pour laquelle on parle de variables d’instances pour les désigner.

S’il n’y a pas d’ambiguïté (objet courant), la variable d’instance est désignée par son nom. Dans le cas contraire, le nom de l’objetdoit être mentionné suivi d’un point et du nom de la variable.

L’instruction

abscisse = p1.ordonnee;

signifie que la valeur du champ ordonnee de l’objet point p1 est affectée au champ abscisse de l’objet point courant. Il s’agit bien icid’une affectation classique puisqu’elle concerne des variables dont les types sont primitifs.

Nous verrons plus tard qu’il est possible de définir des variables de classes pour accueillir des valeursqui sont identiques pour toutes les instances de la classe.

La déclaration des variables

Une règle simple et importante concerne la déclaration des variables, qu’il s’agisse de variables detypes primitifs ou de variables objets: elle peut se situer à n’importe quel endroit mais doit toujoursprécéder l’utilisation de la variable.

Cette règle autorise donc des écritures telles

Carre monCarre = new Carre(5);

double monSalaire, tonSalaire = 4000;

monSalaire = tonSalaire/2;

Page 24: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 18 -

Une réflexion sur la portée des variables sera nécessaire dans la suite de notre réflexion. Signalons déjàqu’une variable cesse d’exister en dehors du bloc dans lequel elle est définie. Un bloc est délimité pardes accolades et peut évidemment contenir d’autres blocs. Ainsi en est-il du bloc de définition d’uneclasse. La définition d’une méthode ou d’un constructeur est également réalisée dans un bloc. Nousverrons que les blocs apparaissent également au niveau des structures de contrôle des actions maispeuvent aussi être définis n’importe où (pour regrouper plusieurs instructions, par exemple).

La méthode main

Si les objets s’envoient les uns les autres des messages de méthodes à exécuter, il faut bien un point dedépart. En réalité, une application peut être vue comme une conversation entre objets. L’initiatrice decette conversation est habituellement une classe qui possède dans sa définition une méthode appeléemain. L’application démarre en demandant à la machine Java virtuelle (JVM) d’exécuter la diteclasse. La méthode main est alors exécutée. Dans les instructions de celle-ci, on peut trouver desinvocations des méthodes des autres objets ou des appels à des constructeurs d’objets d’autresclasses.

En voici un exemple très court. L’invocation d’objet y est réduite à sa plus simple expression mais çanous permet de faire quelques commentaires d’ordre syntaxique et de comprendre comment fonctionneune “application”. Un seul objet est invoqué par la méthode main et c’est un objet prédéfini.

public class Application1{

public static void main(String args[]){

System.out.println("Bienvenue au cours Java!");

}

}

Cet exemple peut paraître bizarre car il ne ressemble guère à ce que nous avons vu jusqu’à présent.La classe ne possède pas de variables d’instance (en fait, elle ne sera jamais instanciée). Son uniqueméthode est la méthode main. Elle est déclarée static et ses paramètres peuvent sembler curieux.

Une méthode est déclarée static lorsqu’il s’agit d’une méthode de classe. Elle ne concerne pas unobjet quelconque de la classe, mais la classe elle-même. Nous verrons que lorsqu’une classe possèdedes variables de classe, elles sont également déclarées static.

Le paramètre args[] est en réalité un tableau de chaînes de caractères (String) auquel nous avonsdonné le nomt args. Ce paramètre désigne un tableau des chaînes de caractères qui peuventéventuellement suivre la commande d’exécution au niveau du shell de commande. Dans la plupart desexemples que nous utiliserons, ces paramètres seront absents.

Page 25: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

28 Les membres de classe peuvent être déclarés statiques. Cela signifie qu’ils ne peuvent plusêtre modifiés par la suite. Dans ce cas, le mot-clé static est utilisé lors de leur déclaration. Ilpeut paraître bizarre qu’une méthode (qui est un membre de classe) soit modifiable. Nousverrons que c’est pourtant souvent le cas lorsque nous aurons parlé d’héritage. La méthodemain d’une classe application est déclarée static.

29 Pour une documentation complète des classes prédéfinies et de leurs méthodes, je vousrecommande l’adresse suivante http://java.sun.com/j2se/1.4/docs/api/index.html.

30 Si vous avez sauté la lecture du paragraphe concernant la machine Java virtuelle en fin duchapitre 1, c’est sans doute le moment d’y revenir.

Chapitre 3 Java: notions de base - 19 -

Vous pourriez également écrire public void main(String[] tableau) ou encore public void main(String table[]). Les syntaxes String[]param et String param[] signifient la même chose à savoir: param est un tableau de String. Notez encore que String est une classeet non un type prédéfini (d’où la majuscule).

Comme toutes les méthodes dynamiques, la méthode println est invoquée sur un objet. Comprendrelequel est un peu plus délicat mais rappelez-vous que nous sommes dans un contexte où tout est objet.out est un champ statique28 (non modifiable) de type PrintStream de la classe non instanciable System.La classe PrintStream est également une classe prédéfinie29.

La variable objet out correspond à la sortie standard (souvent l’écran) et prend comme argument unechaîne de caractère. Le travail de cette méthode est d’envoyer la chaîne de caractères vers cettesortie. Ceci nous fait prendre conscience du fait qu’il existe évidemment un nombre considérable declasses prêtes à l’emploi. Sinon, à quoi cela servirait-il de parler de réutilisabilité! Ces classes sontregroupées dans des packages. Une section est également consacrée à leur utilisation. Le packagejava.lang qui contient la classe System est automatiquement chargé en mémoire à l’exécution d’unprogramme.

L’exécution du programme

Pour pouvoir faire exécuter un programme aussi simple que celui-là, rappelez-vous certaines choses30.Lorsque du code Java est écrit, celui-ci doit être compilé. La compilation produit des octets de code(en anglais, bytecodes), cette espèce de langage universel qui sera interprété au niveau de chaquesystème par un interpréteur Java qui est propre au système utilisé.

La production des octets de codes est réalisée par la commande (le programme) javac. Ceprogramme est localisé quelque part dans le système de fichiers. Sous Windows, par exemple, ilpourrait se trouver dans c:\jdk1.3.1_02\bin. Sous Unix ou Linux, on pourrait le trouver dans/usr/local/bin. La classe qui contient la méthode main doit être enregistrée dans un fichier qui portele même nom qu’elle et l’extension java. Dans notre exemple, le fichier doit s’appelerApplication1.java. Sa compilation

javac Application1.java

Page 26: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 20 -

produira un fichier qui s’appellera Application1.class. Pour faire exécuter l’application, il faut faireappel à l’interpréteur qui se trouve, en principe, dans le même répertoire ou dossier que le compilateur.La commande sera, dans notre cas,

java Application1

Le message s’affiche au niveau de la ligne de commande.

Attention, pour que l’interpréteur trouve la classe à exécuter, il faut lui en fournir le chemin. SousWindows, par exemple, vous modifierez la variable d’environnement classpath en utilisant lacommande set classpath=c:\java\myclasses si la classe ci-dessus se trouve dans ce dossier ou, cequi est souhaitable, vous utiliserez l’option -classpath ... au niveau de la commande.

java -classpath=c:\java\myclasses Application1

Dans un premier temps, nous nous arrangerons pour que les classes se trouvent dans le dossier pardéfaut. Plus tard, lorsque nous parlerons des packages, nous verrons comment avoir une démarcheorganisée à ce propos.

Une application qui crée des objets

L’exemple précédent ne nous montre toutefois pas comment des objets peuvent être créés et s’envoyerdes messages. Maintenant que nous connaissons la manière de faire afficher des résultats, nous allonsfaire en sorte que l’application crée deux points, calcule la distance entre les deux et affiche le résultat.Pour cela, nous ajoutons une méthode distanceJusque qui prendra comme paramètre un autre objetpoint. Lorsque cette méthode sera invoquée sur un objet point, celui-ci calculera la distance qui lesépare du point donné en paramètre.

public class Point{

private int x;private int y;

public Point(){setPoint(0,0);

}

public Point(int a,int b){setPoint(a,b);

}

public int getAbscisse(){return x;

}

public int getOrdonnee(){return y;

}

Page 27: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 21 -

public void setAbscisse(int a){x=a;

}

public void setOrdonnee(int b){y=b;

}

public void setPoint(int a,int b){x=a;y=b;

}

public double distanceJusque(Point p){int diffX=x-p.x;int diffY=y-p.y;return Math.sqrt(diffX*diffX+diffY*diffY);

}

}

La méthode distanceJusque calcule la différence d’abscisse et d’ordonnée. Remarquez que ladistance calculée est celle qui sépare le point courant du point p qui est le paramètre. Pour le pointcourant, on peut utiliser directement x et y qui sont disponibles au niveau de l’objet qui exécute laméthode. Pour se référer aux coordonnées de p, il faut en mentionner le nom suivi d’un point et duchamp concerné.

Les opérateurs sont assez classiques pour qui a déjà un peu programmé. Le chapitre suivant en parledavantage.

Le calcul de la distance fait appel à la méthode sqrt de la classe Math. C’est une méthode qui assezlogiquement est déclarée static.

Voyons maintenant comment l’application peut créer deux points et renvoyer en résultat, la distancequi les sépare.

public class Application2{

public static void main(String args[]){

Point p1=new Point(2,3);Point p2=new Point();System.out.println("La distance entre p1 et p2 est "+p1.distanceJusque(p2)+".");

}

}

Page 28: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

31 Il existe en Java une méthode qui permet de cloner les objets quand c’est nécessaire.

Chapitre 3 Java: notions de base - 22 -

Deux objets de type point sont créés. Le premier point est créé via le constructeur dont la signaturecomprend deux paramètres, le second via le constructeur sans paramètre qui génère automatiquementle point de coordonnée (0,0).

L’argument de la méthode println est une chaîne de caractères. Vous pourriez vous étonner derencontrer l’opérateur de concaténation (+) entre des chaînes de caractères “La distance entrep1 et p2 est ” et “.” et une expression entière qui est le résultat renvoyé par la méthodedistanceJusque. C’est un peu tôt pour donner une explication du bon fonctionnement de cetteinstruction, mais en voici tout de même une rapide qui pourrait convenir à ce stade. L’instruction nepose pas de problème dans la mesure où tous les objets en Java héritent de la classe Object. Cetteclasse possède plusieurs méthodes dont une méthode toString qui convertit l’objet en chaîne decaractères. L’utilisation de l’opérateur + provoque implicitement l’exécution de cette méthode. Dansle cas où l’argument est du type primitif entier, la méthode convertit automatiquement le nombre enchaîne de caractères. C’est précisément ce qui se passe ici et qui permet la concaténation. Nousreviendrons plus en détail sur la méthode toString.

Objet vs variable objet

Nous venons de constater que la création d’un objet se fait exclusivement par l’intermédiaire du mot-clénew suivi du type d’objet et des paramètres éventuels pour un constructeur. Dans l’exempleprécédent, deux constructeurs différents ont été utilisés. Vous devez aussi vous douter qu’il n’est pastoujours nécessaire de définir explicitement un constructeur. Si le constructeur ne doit rien initialiser,un constructeur par défaut peut convenir. Dans ce cas, sa définition est absente de la définition de laclasse.

Mais penchons-nous un peu sur ce qui se passe réellement lors de la création d’un objet. Lorsqu’unevariable objet est déclarée, l’objet n’existe pas encore. Notez qu’il est possible de fusionner ladéclaration du type de la variable objet avec la création de l’objet comme dans notre exemple mais ilest aussi possible de les séparer:

Point p1; // Déclaration de la variable objetp1=new Point(2,3); // Création de l’objet

Observez que Java autorise évidemment les commentaires en fin de ligne (grâce à la séquence decaractères //) mais aussi d’autres types de commentaires que nous décrirons plus loin.

Il convient de ne pas confondre p1 et l’objet que p1 désigne. En réalité, p1 est une référence vers unobjet de type point. C’est un pointeur, une adresse en quelque sorte. A ce propos, mesurez bien laportée qu’aurait une instruction comme p1=p2. Elle signifie ni plus ni moins que “l’objet référencé parp1 est le même que celui référencé par p2". Il n’y a donc pas deux objets mais deux références aumême objet31. Si une méthode effectue une modification sur l’objet en utilisant la référence p1, uneautre méthode consultant ensuite l’objet en utilisant la référence p2 peut constater ces modifications.

Page 29: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 23 -

Point p1=new Point(3,6);Point p2=new Point(1,1);p2=p1;p1.setPoint(4,7);int x=p2.getOrdonnee();System.out.println(x);

La séquence d’instructions qui précède aura comme effet visible d’afficher la valeur 7.

p1 et p2 sont des variables objets qui sont en réalité des références vers des objets de type Point, leursadresses en mémoire. Lorsqu’un objet est référencé parce qu’on invoque une de ses méthodes, parexemple, celui-ci se débrouille pour trouver l’endroit où se situe le code à exécuter.

Ces considérations sont extrêmement importantes pour la suite afin de garantir une programmationcorrecte.

La référence “this”

Nous avons déjà signalé que chaque objet, instance d’une classe, possède ses propres variablesd’instance. Les variables x et y portent les mêmes noms quels que soient les objets pris enconsidération.

C’est assez commode de se référer à l’abscisse d’un point en l’appelant x et à son ordonnée enl’appelant y. Si vous observez le code de la classe Point, vous constaterez que les méthodessetAbscisse, setOrdonnee et setPoint utilisent des paramètres qui portent un autre nom pour éviterla confusion entre les variables. Il est commode de les appeler également x et y, comme dans le codequi suit. La distinction est alors à faire entre les variables d’instance de l’objet courant et les paramètresen utilisant la référence this.

this.x désigne la variable d’instance x de l’objet courant (this)

public class Point{

private int x;private int y;

public Point(){setPoint(0,0);

}

public Point(int a,int b){setPoint(a,b);

}

public int getAbscisse(){return x;

}

public int getOrdonnee(){

Page 30: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 24 -

return y;}

public void setAbscisse(int x){this.x=x;

}

public void setOrdonnee(int y){this.y=y;

}

public void setPoint(int x,int y){setAbscisse(x);setOrdonnee(y);

}

public double distanceJusque(Point p){int diffX=x-p.x;int diffY=y-p.y;return Math.sqrt(diffX*diffX+diffY*diffY);

}

}

Le nouveau code comporte une autre modification. Dans la méthode setPoint, il n’est plus questionde fixer les valeurs des variables d’instance mais de faire appel aux méthodes existantes. Il est plusnaturel de décrire la modification de la coordonnée comme modifications conjointes de l’abscisse etde l’ordonnée.

Le traitement des erreurs

Afin de montrer l’utilité des paramètres d’une méthode main, nous allons améliorer quelque peul’exemple qui précède. Nous introduirons aussi, à titre illustratif à ce stade, un concept qui seradéveloppé avec un plus grand détail dans un des chapitres qui suit: le traitement des erreurs.

Nous souhaitons que notre programme calcule la longueur d’un vecteur. Autrement dit, on fournit lacoordonnée d’un point au niveau de la ligne de commande et le programme calcule la distance quisépare ce point de l’origine.

La commande ressemblera à la suivante java Application3 12 9. Les valeurs 12 et 9 iront remplir letableau args aux positions 0 et 1. Les tableaux en Java comme dans beaucoup d’autres langages sontindexés à partir de 0. Ces informations peuvent donc être récupérées par le programme qui fait sescalculs habituels. Cependant, si les paramètres ne sont pas fournis, la référence à des arguments dutableau qui n’existent pas génère ce que l’on appelle une exception. Hé oui! En POO, il n’y a pasd’erreurs qui se produisent, il y a des objets qui se créent. Et les exceptions sont des objets commeles autres auxquels il est possible de s’adresser. Java, comme d’autres langages de POO, intègre le

Page 31: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

32 Cette classe contient naturellement un seul champ de type entier mais plusieurs méthodes.

33 Si vous êtes à l’aise dans les explications qui ont été fournies jusqu’ici, vous pouvez déjàdeviner qu’il existe des exceptions de toutes sortes. Vous pouvez aussi imaginer que toutesles classes d’exceptions héritent d’une classe générique, la classe Exception.Il n’était donc pas faux d’écrire catch(Exception ai){...}

Chapitre 3 Java: notions de base - 25 -

concept des exceptions. Il fournit une solution élégante à leur gestion. Le code qui suit illustre leprocessus.

public class Application3{

public static void main(String[] args){

try{int x,y;x=Integer.parseInt(args[0]);y=Integer.parseInt(args[1]);Point p1=new Point(x,y);Point p2=new Point();System.out.println("La distance entre p1 et l'origine \est " +p1.distanceJusque(p2)+".");

}

catch(ArrayIndexOutOfBoundsException ai){System.out.println("Vous n'avez pas fourni de valeurs \pour le point.");

}}

}

Les arguments de la méthode main sont toujours des chaînes de caractères. Dès lors, pour pouvoir traiter les informations qu’ilscontiennent comme des nombres, il va falloir les convertir. Cette conversion nécessite une méthode et donc, il faut s’adresser à uneclasse particulière. Cette classe, c’est la classe Integer. Les objets de cette classe sont les entiers. Dit d’une autre manière, danscette classe, les entiers sont assimilés à des objets32. Il est donc possible d’invoquer les méthodes de la classe. Parmi celles-ci, laméthode parseInt qui prend comme argument une chaîne de caractères et renvoie un nombre du type primitif entier. L’expressionInteger.parseInt(...) signifie que la méthode est statique. C’est une méthode propre à la classe Integer qui ne demande pas qu’unobjet soit créé pour pouvoir être invoquée. x et y sont du type primitif entier comme les résultats renvoyés par la méthode.

Le bloc try{...} contient les instructions qui feront l’objet d’un examen plus attentif des erreurs éventuelles. Le bloc catch(...){...} contientles instructions qui gèrent l’exception qui a été générée dans ce cas. L’exception qui peut avoir été créée ici résulte de l’absenced’un ou des deux arguments dans l’appel de la méthode main. Dans ce cas, l’instruction

x=Integer.parseInt(args[0]);

ou l’instruction

y=Integer.parseInt(args[1]);

provoque la consultation d’une valeur du tableau dont l’index est hors limites. L’exception générée est du type (prédéfini)ArrayIndexOutOfBoundsException33. L’exception est récupérée par une instruction qui, dans ce cas, affiche un message d’absencede données.

Page 32: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 26 -

La gestion des exceptions par Java est une façon élégante de résoudre les problèmes à l’endroit précisoù ils peuvent se poser. Quand un bloc d’instructions est critique, l’utilisation du try - catch est unebonne précaution. Nous reviendrons beaucoup plus en détail sur cette opportunité de gérer lesexceptions dans les chapitres qui suivent.

Page 33: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

34 Donnez leur un nom et des arguments. Pour ce qui est des traitements qu’elles doiventrenfermer, contentez-vous de {...}.

35 L’imagination peut ne pas s’arrêter là. On peut construire une droite à partir d’un point et dedeux droites sécantes en un autre point, etc.

Chapitre 3 Java: notions de base - 27 -

Exercices

Ex1 Vous allez devoir gérer une base de données des personnes qui fréquentent, à titre professionnelou non, votre établissement. Imaginez la création d’une classe Personne, les champs et les méthodesqui pourraient correspondre à sa définition. Si c’est possible, procédez par composition. Par exemple,une personne possède une adresse; qu’est-ce qu’une adresse? Ne faudrait-il pas créer une autreclasse? Comment cette dernière serait-elle définie?

Ex2 Dans le cadre de la programmation d’un jeu de cartes, imaginez une classe CarteAJouer et uneclasse JeuDeCartes. Pensez à la possibilité d’utiliser des tableaux. Quels en seraient les champs?Quelles en seraient les méthodes respectives34?

Ex3 Imaginez une classe Parallélogramme. Quels seraient les champs intéressants à définir?Décrivez différents constructeurs, selon les données disponibles. Prévoyez quelques méthodes d’accèset d’altération. Décrivez plusieurs méthodes pour le calcul du périmètre, de la surface, de la longueurdes diagonales.

Ex4 Supposez que nous travaillions dans un espace à deux dimensions. En réutilisant la classe Pointque nous venons de définir, créez une classe Droite.

• Quelles informations devrait-elle renfermer?

• Imaginez plusieurs constructeurs possibles: deux points, un point et un coefficient angulaire, un pointet une droite qui donne la direction35.

• Prévoyez les cas à problème: le point se trouve sur la droite, les deux points sont identiques (mêmescoordonnées),...

• Imaginez différentes méthodes:

- une méthode qui vérifie si un point appartient à une droite

- une méthode qui vérifie si deux droites sont parallèles

- ...

• Améliorez la classe Point en lui ajoutant une méthode qui vérifie si un point est dans l’alignement dedeux autres points donnés.

Page 34: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 28 -

Attention, nous avons défini la classe Point en imaginant que les abscisses et ordonnées des pointsétaient toujours des nombres entiers. C’est évidemment fort contraignant et nous corrigerons cettesituation par la suite. Toutefois, ne modifiez rien à cela pour l’instant car cela nous permettra dedécouvrir certaines choses à propos de la manière de calculer de Java.

Ebauche de solution et commentaires

Tâchons d’apporter un maximum de réponses au problème qui vient d’être décrit. Elaborons d’abordla classe Droite et ses constructeurs. Cet exemple illustre bien le fait que la construction d’un objetpeut se faire sur base d’éléments de connaissance différents.

Dans cet exemple apparaissent pour la première fois des structures de test. Leur syntaxe n’est pas trèscompliquée. Un chapitre leur sera consacré ainsi qu’aux structures de contrôle en général.

Dans le chapitre qui suit, il sera également question des opérateurs. Nous en découvrons ici quelques-uns. Les opérateurs de comparaison: égalité (==), inégalité (!=), inférieur à (<), l’opérateur logique ET(&&), les opérateurs arithmétiques: soustraction (-), division (/), multiplication (*), concaténation (+)et l’opérateur d’affectation élargie (+=).

public class Droite{

Point unPoint;int saPente;

public Droite(Point p, int ca){unPoint=p;saPente=ca;

}

public Droite(Point p1, Point p2){unPoint=p1;if (p1.equals(p2)){

saPente=0;}else{

saPente = (p1.getOrdonnee() - p2.getOrdonnee()) /(p1.getAbscisse() - p2.getAbscisse());

}}

public Droite(Point p, Droite d){unPoint=p;saPente=d.saPente;

}

public boolean passePar(Point p){if (unPoint != p && saPente != (unPoint.getOrdonnee() -p.getOrdonnee()) / (unPoint.getAbscisse() - p.getAbscisse())){

return false;}else{

Page 35: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

36 Un autre choix stratégique aurait pu être fait.

Chapitre 3 Java: notions de base - 29 -

return true;}

}

public String toString(){String aux;int b= unPoint.getOrdonnee() - saPente * unPoint.getAbscisse();if (saPente == 0){

aux = "y = " + b;}else{

aux = "y = " + saPente + "x ";if (b<0){

aux+=b;}else{

aux += "+" + b;}

}return aux;

}

public boolean estParalleleA(Droite d){if (saPente == d.saPente)return true;elsereturn false;

}

public String intersectionAvec(Droite d1){

// Cette méthode devrait renvoyer un message indiquant// si les droites sont parallèles distinctes, confondues// ou précisant la coordonnée de leur point d'intersection.

return "Inactif";}

}

Le constructeur de la droite à partir de deux points s’assure que les deux points ne sont pas identiques.Dans le cas contraire, la droite est considérée comme horizontale36. La vérification de l’égalité de deuxobjets ne peut se faire par l’utilisation de l’opérateur d’égalité qui se contenterait de vérifier l’égalité desréférences. Or les deux objets pourraient être identiques alors que les références sont différentes.C’est ce qui justifie l’emploi de la méthode equals qui est évidemment une méthode prédéfinie de laclasse Object dont héritent automatiquement, rappelons-le, tous les objets. La pente est calculée onne peut plus classiquement sur base des abscisses et ordonnées des deux points, ce qui nécessite unappel aux méthodes d’accès de la classe Point.

Page 36: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

37 Le cas où le terme indépendant est nul n’a pas été traité. L’amélioration de la méthode pourrépondre à cette remarque vous est laissée à titre d’exercice.

Chapitre 3 Java: notions de base - 30 -

Le troisième constructeur est en quelque sorte récursif. Sa définition demande un paramètre qui est unedroite. Elle n’aurait pas de sens sans l’existence d’au moins un autre constructeur. Observez que tousles constructeurs ont une signature différente, ce qui est une obligation.

La méthode passePar vérifie si un point appartient à une droite ou non. La valeur renvoyée est unbooléen. De par la manière dont le test est effectué, cette méthode donnera souvent de mauvaisrésultats. Si le point donné est identique au point qui caractérise la droite, le test s’arrête là et la valeurrenvoyée est true. La valeur renvoyée est false si les points sont différents et que le calcul ducoefficient angulaire donne un résultat différent de la pente de la droite. Cet algorithme peut paraîtrecorrect. Il ne l’est pas dans la mesure où la division entre deux entiers fournira un résultat entier.L’application AppDroites dont le code suit permet de mettre cette erreur en évidence.

Le calcul du coefficient angulaire n’est pas nécessaire si l’équation de la droite est connue sous la formey = mx + p. La valeur de p n’est pas connue mais pourrait être calculée à la construction de la droite.Le test serait alors plus simple à exécuter. Il suffirait de vérifier que les coordonnées du point vérifientl’équation ce qui résoudrait les problèmes dans le cas où m et la coordonnée d’un point sont donnés.Il n’y aurait plus de division à effectuer. Dans le cas ou m est calculé par le constructeur, des erreursd’arrondi pourraient encore générer des réponses fausses, mais par la faute de la x- ième décimale.

Ceci montre que la structure d’une classe d’objets, de même que la définition des méthodes peuventévoluer en fonction des problèmes rencontrés. Parfois aussi, des améliorations de la structurepermettent des économies d’échelles. Voyez, à la fin de cet exercice, comment on peut envisager uneévolution de la structure précédente.

La méthode toString a pour objectif de renvoyer l’équation de la droite sous forme de chaîne decaractères. Cette méthode est en réalité réécrite puisqu’elle est prédéfinie dans la classe Object.Toutefois, le fait qu’elle soit réécrite est ici anecdotique. Le terme indépendant (p dans y=mx+p) estcalculé et l’affichage est adapté selon que les valeurs de m et p sont positives ou négatives37. Signalonsencore que l’apparition d’un nombre dans une expression contenant des chaînes de caractèresprovoque la conversion implicite de ces nombres en chaînes de caractères.

La méthode estParalleleA ne demande pas de commentaires bien particuliers.

L’application est définie dans une classe qui porte le nom de AppDroites. Dans la méthode main decette classe, les points et les droites sont créés de manières variées.

public class AppDroites{

// Que va faire afficher cette application?

public static void main(String [] args){

Point p1=new Point(12,3), p2=new Point(-1,1);Droite d1=new Droite(p1,3), d2=new Droite(p1, p2),

Page 37: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 31 -

d3=new Droite(new Point(4,2), d1);Point p4=p1;Droite d4=new Droite(p1, p4);

System.out.println("Droite D1: " + d1);System.out.println("Droite D2: " + d2);System.out.println("Droite D3: " + d3);System.out.println("Droite D4: " + d4);

}}

Dans l’état actuel du programme, l’équation affichée pour la droite d2 ne sera pas correcte car la pentecalculée sera nulle. Les corrections qui suivent ont notamment pour objectif de résoudre ce problème.

Evolution des classes Point et Droite

La classe correspondant à l’application reste inchangée.

Dans la classe Point, les abscisse et ordonnée sont cette fois de type float de même que les valeursrenvoyées par les méthodes d’accès et les paramètres numériques des autres méthodes.

public class Point{

private float x;private float y;

public Point(){setPoint(0,0);

}

public Point(float a,float b){setPoint(a,b);

}

public float getAbscisse(){return x;

}

public float getOrdonnee(){return y;

}

public void setAbscisse(float x){this.x=x;

}

public void setOrdonnee(float y){this.y=y;

}

public void setPoint(float x,float y){setAbscisse(x);setOrdonnee(y);

Page 38: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 32 -

}

public double distanceJusque(Point p){float diffX=x-p.x;float diffY=y-p.y;return Math.sqrt(diffX*diffX+diffY*diffY);

}

public String toString(){return "(" + x + "," + y + ")";

}}

Dans la classe Droite, les changements sont plus importants. Un nouveau champ apparaît, c’est lechamp termeIndependant qui est de type float comme saPente. Il est calculé par la méthodecalculduTermeIndependant. Les constructeurs sont également modifiés. Les méthodes passeParet toString peuvent être réécrites autrement.

public class Droite{

Point unPoint;float saPente;float termeIndependant;

public Droite(Point p, float ca){unPoint=p;saPente=ca;termeIndependant=calculDuTermeIndependant();

}

public Droite(Point p1, Point p2){unPoint=p1;if (p1.equals(p2)){

saPente=0;}else{

saPente=(p1.getOrdonnee() - p2.getOrdonnee()) /(p1.getAbscisse() - p2.getAbscisse());

}termeIndependant=calculDuTermeIndependant();

}

public Droite(Point p, Droite d){unPoint=p;saPente=d.saPente;termeIndependant=calculDuTermeIndependant();

}

private float calculDuTermeIndependant(){return unPoint.getOrdonnee() - saPente * unPoint.getAbscisse();

}

Page 39: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 33 -

}

Observez enfin qu’il est aussi pensable de créer une droite à partir de son équation. Nous vous laissonsl’écriture du constructeur à titre d’exercice supplémentaire.

Ex 5 Un étudiant peut s’inscrire à un maximum de 10 cours ou modules. Chaque cours est donné parun professeur titulaire ou un assistant et suit un calendrier qui indique les jours et les périodes horairesauxquels il a lieu. Imaginez une structure minimale (classes, champs et méthodes) qui décrit cettesituation. Si vous deviez définir une méthode qui renseigne si un étudiant est occupé à suivre un cours,à un moment précis (date et heure), comment vous y prendriez-vous? Il n’est pas nécessaire dedécrire, à ce stade, les algorithmes qui garnissent les méthodes comme la vérification de la compatibilitédes horaires lors d’une inscription à un nouveau module. En revanche, il est possible de déclarer lesméthodes et leurs signatures.

Ebauche de solution et commentaires

Ce petit exemple fait déjà percevoir la nécessité de faire précéder la construction d’une application,d’une sérieuse étude des données du système d’information et de leurs relations. C’est sans doute làque se marque, de la manière la plus évidente, la différence entre la programmation impérative et laprogrammation orientée objet. L’importance accordée par la POO au SI implique que les traitements(méthodes) sont imaginés par la suite en fonction des classes d’objets et ne constituent plus, commedans la programmation impérative, la base de la programmation.

L’exemple montre également, si nécessaire, que la programmation de points plus délicats faisant appelà l’algorithmique classique peut être différée en permettant de se consacrer aux “macro-traitements”.Cette programmation pourrait d’ailleurs très bien être confiée à une tierce personne.

La création de certaines classes semblent s’imposer: une classe Etudiant et une classe Module. Onpeut aussi imaginer une classe Enseignant qui regrouperait les professeurs et leurs assistants, bien que,dans le cadre strict de ce problème, on puisse en faire l’économie. Nous l’avons toutefois décrite dansla solution.

class Etudiant{

private String nom;private String prenom;private int nombreCours=0;private Module [] occupation;

public Etudiant(String nom, String prenom){this.nom=nom;this.prenom=prenom;

}

public boolean inscriptionAuCours(Module m){

// OK si le nombre de modules auquel l'étudiant est déjà

Page 40: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 34 -

// inscrit est inférieur à 10 et s’il n’y a pas de problème// avec les horaires de ces autres modules

// La méthode qui suit pourra éventuellement être utile.// La méthode effectue un traitement et renvoie une valeur// booléenne qui renseigne si le traitement s’est bien passé.

}

public boolean estOccupe(Date moment){

// Le paramètre est du type Date prédéfini.

// La vérification renverra un booléen.}

}

class Module{

private String code, titre;private Date debut, fin;private Enseignant titulaire, assistant;

public Module(String c, String t, Date d, Date f){code=c;titre=t;debut=d;fin=f;

}

public Module(String c, String t, Enseignant tit, Enseignant assist, Dated, Date f){

code=c;titre=t;titulaire=tit;assistant=assist;debut=d;fin=f;

}

// D'autres constructeurs sont possibles (dates non encore fixées,...)

public String getCode(){return code;

}

public Date getDateDebut(){return debut;

}

public Date getDateFin(){return fin;

}

// D'autres méthodes d'accès et d'altération peuvent être définies

Page 41: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 35 -

// en fonction des besoins réels

}

class Enseignant{

private String nom, prenom;private String matricule;

public Enseignant(String n, String p, String m){setName(n,p);setMatricule(m);

}

public void setName(String nom, String prenom){this.nom=nom;this.prenom=prenom;

}

public void setMatricule(String matricule){this.matricule=matricule;

}

public String getNom();{return nom;

}

public String getPrenom();{return prenom;

}

public String getMatricule();{return matricule;

}}

L’étudiant possède un nom et un prénom et est occupé par des modules de cours. Lorsque lesmodules sont construits, on ne connaît pas nécessairement les enseignants mais bien leur code et leurtitre. Il est possible qu’on ne connaisse pas non plus les dates et heures correspondantes.

La référence this est quelque fois utilisée. Il est possible de l’éviter en imaginant d’autres noms devariables.

La classe Date est, vous vous en doutez, une classe prédéfinie. Nous aurons l’occasion de découvrirplus en avant sa structure et ses méthodes.

Dans le code proposé, il n’y a pas d’application qui exploite les structures mises en place. Nous noussommes limités à créer les classes et les méthodes qui pouvaient caractériser le SI décrit.

Ex 6 La somme de deux nombres entiers

Page 42: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 36 -

Cet exercice résolu est une réponse, à la fois très sérieuse et pouvant faire sourire au défi lancé par unami lors d’une discussion intéressante à propos de la POO: “Comment programmer la somme de deuxnombres entiers en OO?”.

Si cette question ressemble plus à une gageure qu’à un vrai problème, elle montre toutefois que lesapproches impérative et objet sont assez différentes, de même d’ailleurs que leurs objectifs.

Si le problème est d’obtenir la somme de ces deux nombres, sans plus, l’approche impérative fournitune réponse assez évidente. L’existence d’un opérateur d’addition et son utilisation fournissent uneréponse assez immédiate. En pseudo-code:

lire a, bsomme w a + bécrire somme

Voici ce que cela pourrait donner en Java:

public class AdditionDEntiersImperatif{

public static void main(String [] args){int n1 = 12;int n2 = 54;

int somme = n1 + n2;

System.out.println("La somme des nombres n1 et n2 est " + somme+ ".");

}}

Mais en approche objet, le point de départ est essentiellement une description du systèmed’information, indépendamment des traitements que l’on souhaite développer. On admettra donc qu’ily a des nombres entiers (une classe de) dont un des champs de données sera inévitablement la valeur.

class Entier{private int valeur;

On pourrait d’ailleurs y ajouter un constructeur élémentaire.

public Entier(int v){valeur=v;

}

Une réflexion ultérieure sur la nature des messages à adresser à un nombre conduira sans doute àadmettre qu’un nombre devrait pouvoir s’additionner à un autre nombre.

public Entier plus(Entier e){return new Entier(valeur + e.valeur);

}

Page 43: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 37 -

public int getValeur(){return valeur;

}}La méthode plus renvoie une référence de type Entier. Il s’agit d’un objet sans nom dont le champvaleur contiendra la somme (au sens propre du terme) des contenus des champs valeur des deuxnombres de type Entier.

La méthode getValeur est nécessaire compte tenu du type d’accès au champ valeur (private).

Cette réflexion peut paraître un peu lourde mais il ne faut pas perdre de vue que d’autres élémentspeuvent caractériser un nombre et que d’autres traitements utiles peuvent apparaître progressivement.On pourrait, par exemple, demander à un nombre de s’afficher en toutes lettres, de s’écrire en chiffresromains,...

Pour compléter la réponse, on peut écrire une petite application du genre de celle qui suit.

public class AdditionDEntiersObjet{

public static void main(String [] args){Entier n1 = new Entier(12);Entier n2 = new Entier(54);

System.out.println("La somme des nombres n1 et n2 est " +n1.plus(n2).getValeur());

}}

Voici donc une version complète de l’addition de deux nombres entiers en approche objet. On devineque le terrain est prêt pour des extensions et des améliorations. Notez encore que des classesenveloppant les types primitifs (wrapping classes) sont prédéfinies en Java. Elles portent les noms deces types en commençant par une majuscule évidemment: Boolean, Float,... Deux exceptions à cettehomonymie: le type int est emballé dans la classe Integer et le type char dans le classe Character.

public class AdditionDEntiers{

public static void main(String [] args){Entier n1 = new Entier(12);Entier n2 = new Entier(54);

System.out.println("La somme des nombres n1 et n2 est " +n1.plus(n2).getValeur());

}}

class Entier{private int valeur;

public Entier(int v){valeur=v;

Page 44: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 3 Java: notions de base - 38 -

}

public Entier plus(Entier e){return new Entier(valeur + e.valeur);

}

public int getValeur(){return valeur;

}}

Ex 7 Définissez une classe Heure capable d’instancier tous les moments de la journée. Imaginez diversconstructeurs sur base de données différentes:

• heure minute (0-24)• heure minute seconde (0-24)• heure minute (0-12)• heure minute seconde (0-12)• une valeur entière

Imaginez diverses méthodes:

• renvoi de l’heure, de la minute, de la seconde,... sur base de la valeur et l’inverse• calcul de la durée entre deux informations de type Heure• ajout d’une durée à une Heure pour obtenir une autre Heure• ...

Ecrivez une classe d’application.

Page 45: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 39 -

Chapitre 4 Quelques compléments utiles

Le passage des paramètres en Java

Les deux principales manières de passer des paramètres en programmation sont le passage parvaleur et le passage par référence.

Pour éviter toute ambiguïté à ce propos, signalons qu’un passage par valeur fournit à la méthode,la fonction, la procédure, une copie de la valeur du paramètre . Il est donc impossible que la valeureffective du paramètre soit modifiée par la méthode, la fonction, la procédure puisque les traitementssont effectués sur une copie.

Le passage par référence, au contraire, fournit l’emplacement de la variable concernée, ce qui permetà la méthode, la fonction, la procédure de modifier cette variable.

Que se passe-t-il en Java? Pour bien le comprendre, il faut assimiler deux points importants.

Premier point

En Java, (et contrairement à Pascal ou C++) le passage par valeur est le seul existant. Il n’ya pas de passage par référence.

Supposons qu’il existe une méthode

public static void auCarre(int x){x = x * x;

}

Une séquence d’instructions telle

int x=10;auCarre(x);System.out.println(x);

fournira à l’affichage la valeur 10.

L’explication est la suivante: la méthode auCarre reçoit une copie de la valeur du paramètre formel x.La valeur de cette copie est élevée au carré, puis la méthode se termine détruisant la copie. En voiciune illustration plus parlante:

public class ParValeur{

public static void main(String [] args){int x=10;auCarre(x);System.out.println("APRES SORTIE DE LA METHODE: x vaut " + x);

Page 46: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

38 C’est de cette confusion que naissent des erreurs fréquentes dans la littérature faisant dire àtort qu’en Java, les paramètres sont passés par référence. C’est une ineptie.

Chapitre 4 Quelques compléments utiles - 40 -

}

public static void auCarre(int x){System.out.println("DEBUT DE LA METHODE: x vaut " + x);x = x * x;System.out.println("FIN DE LA METHODE: x vaut " + x);

}}

Affichage:

DEBUT DE LA METHODE: x vaut 10FIN DE LA METHODE: x vaut 100APRES SORTIE DE LA METHODE: x vaut 10

Second point

Les variables objets en Java sont des adresses mémoires d’objets. On dit d’ailleurs aussi queles variables objets contiennent des références (aïe!38) à des objets. Ceci n’est pas fait pour faciliterla compréhension. Vous comprendrez toutefois, en combinant les deux choses, que lorsque desobjets sont passés en paramètres, ce sont les adresses de ces objets qui sont passées et quele passage de ces adresses se fait par valeur. Une méthode ne peut donc modifier l’adresse d’unobjet puisque ce qui est fourni à la méthode est une copie de la valeur de l’adresse, copie quidisparaîtra à la fin de la méthode.

Dès lors, la méthode disposant d’une copie de l’adresse des objets fournis en paramètres peut, si elley a accès, modifier les champs de données de ces derniers.

L’application qui suit montre ce qui est possible et ce qui ne l’est pas. On observe notamment que lepassage de paramètres objets permet d’agir sur l’état de ces objets mais ne permet pas d’en modifierla référence. La méthode permute ne fonctionne donc pas comme on pourrait s’y attendre.

Pour les besoins de la cause, la classe Point a été modifiée et transformée en une classe Point2 dontvoici la définition. Deux nouvelles méthodes y apparaissent, une méthode qui renvoie le symétriqued’un point et une méthode statique (méthode de classe) qui est sensée permuter deux points. C’estcette dernière qui pose problème car elle tente de modifier (permuter) les références des objets, ce quimarche bien à l’intérieur de la méthode mais est sans effet en dehors.

public class Point2{

private float x;private float y;

public Point2(){setPoint(0,0);

Page 47: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 41 -

}

public Point2(float a,float b){setPoint(a,b);

}

public float getAbscisse(){return x;

}

public float getOrdonnee(){return y;

}

public void setAbscisse(float x){this.x=x;

}

public void setOrdonnee(float y){this.y=y;

}

public void setPoint(float x,float y){setAbscisse(x);setOrdonnee(y);

}

public String toString(){return "(" + x + "," + y + ")";

}

public Point2 symetrique(){Point2 sym = new Point2(-x,-y);return sym;

}

public static void permute(Point2 p1, Point2 p2){Point2 aux = p2;p2 = p1;p1 = aux;

// Les références aux deux objets ont été permutées à l'intérieur de// la méthode.

System.out.println("APRES PERMUTATION ET AVANT LA SORTIE DE \LA METHODE...");System.out.println("p1: " + p1);System.out.println("p2: " + p2);System.out.println();

}}

Page 48: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 42 -

Voici l’application qui met en oeuvre cette nouvelle classe et met en évidence le passage par valeur desparamètres.

public class AppPoint2{

public static void main(String [] args){

Point2 p1=new Point2(3,-1);Point2 p2=new Point2(2,2);

// L'invocation de la méthode "symetrique" sur l'objet p1 renvoie un// objet anonyme qui est "imprimé ".

System.out.println("LE SYMETRIQUE DE " + p1 + " EST " +p1.symetrique());System.out.println();

System.out.println("p1: " + p1);System.out.println("p2: " + p2);System.out.println();

// La permutation des deux objets est une permutation de copies de// ces objets.

Point2.permute(p1,p2);

// Lorsque la méthode se termine, ces copies disparaissent.// La permutation est donc sans effets apparents.

System.out.println("APRES PERMUTATION ET SORTIE DE LA \METHODE...");System.out.println("p1: " + p1);System.out.println("p2: " + p2);System.out.println();

// Il est pourtant possible de modifier de manière définitive la// coordonnée d'un point par l'intermédiaire d'une méthode.

p1.setPoint(p2.getAbscisse(),p2.getOrdonnee());

System.out.println("APRES MODIFICATION...");System.out.println("p1: " + p1);System.out.println("p2: " + p2);System.out.println();

}}

La classe String

Page 49: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

39 La méthode substring est particulière. Le premier argument est l’index du premier caractère àextraire (en commençant la numérotation par 0), le second est celui du premier caractère à nepas extraire.

Chapitre 4 Quelques compléments utiles - 43 -

Si vous êtes habitués des langages de programmation, vous aurez sans doute été étonnés de ne pastrouver dans les types primitifs un type “chaîne de caractères”. Ce type n’existe pas en Java, toutefois,il existe une classe String dans la bibliothèque standard de Java. La manière habituelle de travaillerest en effet de regrouper les classes dans des librairies et d’importer celles-ci lorsqu’elles sontnécessaires.

Les librairies dont nous parlerons davantage plus tard sont organisées en arborescence et les chemins sont décrits un peu commeles répertoires mais en utilisant le point. La classe String (remarquez la majuscule) fait partie de la librairie ou du package java.lang.Des déclarations telles celles qui suivent sont valides sans que des instructions d’importation de ce package ne soient nécessaires.

String nom;

String societe=”CeFIS”;

String message=””;

La définition de cette classe comprend des champs et des méthodes que le programmeur peutévidemment utiliser à souhait. En voici quelques exemples.

String message = ”Bienvenue au cours Java”;

String mot = message.substring(13,18)39;

int l = message.length();

char c = mot.charAt(3);

int s = l + message.indexOf(“Java”);

La plupart de ces instructions pourraient être détaillées pour séparer la déclaration des variables de leurs affectations. Le langageoffre cette souplesse de pouvoir réaliser les deux choses en une seule instruction.

int l, s;l = message.length();s = message.indexOf(“Java”);s += l;

Cette dernière instruction est équivalente à

s = s + l;

Comme avec beaucoup d’autres langages, la concaténation des chaînes de caractères s’effectue grâceà l’opérateur +.

Page 50: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

40 Par défaut de réécriture au sein d’une nouvelle classe, la méthode toString() renvoie le nom dela classe et une adresse en mémoire. Il est courant de redéfinir cette méthode au sein desclasses dont on aimerait afficher les objets sous forme de chaînes de caractères. Cetteremarque anticipe sur les mécanismes d’héritage et de polymorphisme dont nous parleronsdans les chapitres qui viennent.

Chapitre 4 Quelques compléments utiles - 44 -

La séquence suivante

String message = “Bienvenue au cours Java”;String auteur = “Etienne Vandeput”;String entete = message + ” (“ + auteur + “)”;System.out.println(entete);

provoquera l’affichage: Bienvenue au cours Java (Etienne Vandeput)

De plus, toute concaténation d’une chaîne de caractères avec un élément d’un autre type provoque uneconversion automatique de ce type.

String marque=”Opel”;String type=”Vectra”int cylindree=2000;System.out.println(marque + “ ” + type + “ ” + cylindree);

provoquera l’affichage: Opel Vectra 2000

La méthode toString()

Que se passe-t-il, dès lors, si le type de l’élément de la concaténation n’est pas un type primitif maisun type d’objet? Tous les classes héritant de la classe Object, et cette dernière possédant une méthodetoString, tout objet peut donc être affiché sous forme d’une chaîne de caractères40.

Nous avons déjà ajouté à la définition de la classe Point:

public String toString(){return "(" + x + "," + y + ")";

}

De la sorte, les instructions

Point p1=new Point(3,4);System.out.println(“La coordonnée du point est “ + p1 + “.”);

provoquent l’affichage du message La coordonnée du point est (3,4).

Page 51: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

41 Pour que la chaîne renvoyée s’affiche sur plusieurs lignes, utilisez la séquence \n à l’intérieurde la chaîne.

Chapitre 4 Quelques compléments utiles - 45 -

La chaîne renvoyée par la méthode toString d’un objet est laissée à l’appréciation du programmeur.Pour une droite, nous avons choisi de fournir une équation. Pour une personne, nous pourrions fournirune partie de ses coordonnées41.

Les méthodes statiques et les variables de classe

Les exemples qui ont été pris jusqu’ici ont montré des classes dont les instances sont égalementstructurées en champs et en méthodes. La classe A permet de créer des objets de type A. Sadéfinition pourrait ressembler à ce qui suit:

class A{B champ1;C champ2;...public A(){...} // constructeur d’objet de type Apublic D methode1(){...}public void methode2(...){...}...}

class Application{public static void main(String[] args){

...A objetDeTypeA = new A();...

}

L’objet créé possède des champs nommés champ1 (de type B), champ2 (de type C),... La méthodenommée methode1renvoie une référence vers un objet de type D alors que la méthode nomméemethode2 ne renvoie rien. La première méthode ne prend pas de paramètres alors que ceux qui sontpris par la deuxième méthode ne sont pas détaillés. La classe Application possède une méthode maindont une des instructions crée un objet de type A. Voici, par exemple, une manière d’invoquer une desméthodes:

...D objetDeTypeD = new D(); // Utilisation du constructeur de la classe DobjetDeTypeD = objetDeTypeA.methode1();...

Les méthodes sont invoquées sur des objets précédemment créés. ObjetdeTypeA est une référencevers un objet de type A. L’expression objetDeTypeA.methode1() a donc du sens. Cette méthoderenvoie une référence vers un objet de type D. L’affectation objetDeTypeD =objetDeTypeA.methode1() est donc correcte.

Page 52: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 46 -

Voilà comment fonctionne donc de manière dynamique un programme Java. La méthode principaled’une classe s’exécute provocant la création de nouveaux objets ayant les mêmes champs de donnéescontenant des valeurs propres et dont il sera possible d’invoquer les méthodes. Mais tout ne fonctionnepas complètement de cette manière.

Faisons les observations suivantes:

• il est possible que certains champs contiennent une information qui caractérise la classe plutôt quechacun de ses objets;

• il est possible que certaines méthodes ne demandent pas que des objets soient créés pour agir.

Illustrons d’un exemple chacun de ces deux cas. Vous avez décidé de mettre de l’ordre dans votrecollection de cassettes vidéos. Outre certains renseignements utiles tels le titre du film, la dated’enregistrement, etc., vous souhaiterez sans doute les numéroter. Le numéro d’identification doit êtreunique et vous n’avez aucune envie de retenir les numéros que vous avez déjà utilisés. La classe peutconserver cette information pour vous. Lors de la construction d’un nouvel objet Cassette,l’information concernant le prochain numéro à utiliser (et qui est une information propre à la classe) seraexploitée pour éviter un double emploi.

On dira que le champ contenant cette information est statique car l’information qu’il contient nedépend pas d’un objet particulier mais est lié à la classe.

Voici à quoi pourrait ressembler une utilisation de cette information:

Dans la définition de la classe Cassette, on trouvera:

public static int prochainNumero = 1; // champ statique de la classe Cassettepublic int numero;// champ dynamique de type primitif entierpublic String Titre;public Date enregistreLe;...

Dans la partie de code qui crée un nouvel objet Cassette, on trouvera:

Cassette c = new Cassette();// création d’un objet c de type Cassette c.numero = Cassette.prochainNumero++// affectation du numéro à la cassette sur base du dernier numéro utilisé

Quelques commentaires à ce propos:

• la variable prochainNumero est déclarée statique ce qui veut dire qu’elle caractérise la classe à unmoment donné du cycle d’exécution et ce qui ne veut pas dire qu’il est impossible de la modifier

• il existe une classe Date prédéfinie

Page 53: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

42 Extrait de la documentation Java: “The class Math contains methods for performing basicnumeric operations such as the elementary exponential, logarithm, square root, andtrigonometric functions.”

Chapitre 4 Quelques compléments utiles - 47 -

• les champs sont déclarés public dans cet exemple car on suppose que les affectations dont ils sontles objets se font à l’extérieur de la classe (alors que les constructeurs pourraient s’en charger)

• c.numero est une variable d’instance du type primitif entier alors que prochainNumero est unevariable de classe de même type, donc l’affectation a du sens

L’écriture a = b++ cache une double instruction. La valeur de b est d’abord affectée à a puis est augmentée d’une unité. Il estaussi possible d’écrire a = ++b qui augmente b avant d’en affecter la valeur à a.

Il n’y a pas que les champs qui puissent être déclarés statiques. Les méthodes peuvent l’être aussi.Comme c’était le cas pour les champs, il suffit qu’elles soient propres à la classe et non à un objetparticulier. Vous avez déjà rencontré un exemple de méthode statique ou méthode de classe, c’est laméthode sqrt de la classe prédéfinie Math. Cette classe n’a pas d’instances possibles, mais comprendun certain nombre de fonctionnalités intéressantes. Ainsi, pour faire effectuer un calcul un peuparticulier, on peut s’adresser à la classe Math42 et invoquer une méthode en lui passant les nombresen arguments.

La classe Math contient notamment la déclaration suivante:

static double sqrt(double a){...}

qui définit la méthode sqrt comme renvoyant un nombre du type primitif double sur base de la fourniture d’un autre nombre de typedouble.

Comme pour référencer un champ statique, l’invocation d’une méthode statique se fait auprès de laclasse et non d’un objet particulier.

return Math.sqrt(diffX*diffX+diffY*diffY);

L’utilisateur (le programmeur dans ce cas) peut définir lui-même des méthodes de classes qu’ilconsidère comme statiques. Par exemple, une méthode qui fournirait le prochain numéro à utiliser pourune cassette serait statique en ce qu’elle ne concerne qu’un champ statique et n’a pas besoind’informations concernant un quelconque objet.

Il y a une différence fondamentale entre les méthodes statiques et les méthodes normales qui sontassociées à un processus dit de liaison dynamique . Cette différence sera mise ne valeur lors de l’étudedes mécanismes d’héritage et de polymorphisme. Nous verrons que lorsqu’une méthode est invoquéesur un objet (qui en est implicitement un paramètre), le compilateur doit déterminer la définition idéale

Page 54: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

43 L’extension de la définition d’une classe sera examinée dans le détail. Toutefois, il meparaissait intéressant de mentionner dès maintenant la possibilité de rendre les classes et les

Chapitre 4 Quelques compléments utiles - 48 -

de la méthode à appliquer. Dans une classe, une méthode peut en effet être définie de plusieursmanières et est même fréquemment redéfinie dans ses sous-classes. Pour ce qui est des méthodesstatiques, cette résolution n’a pas de raison d’être.

Les champs et les méthodes déclarés finals

Il arrive que certains champs d’une classe doivent être considérés comme inaltérables. C’est vrai pourcertains champs static. Un exemple évident est la définition d’une constante. Au fond, une constanten’est rien d’autre qu’une variable qui ne changera plus dans la suite de l’exécution du programme. C’estvrai aussi pour les variables d’instance. Lorsqu’un objet est créé, le programmeur peut souhaiter quela valeur de certains champs ne soit plus modifiée pendant toute la durée de vie de cet objet: le nomd’une personne, son numéro de matricule, le numéro de TVA d’une entreprise...

Le mot-clé est le mot final. En voici des utilisations possibles:

class Application{private static final int MAXIMUM = 1000;...

}

Il s’agit ici d’une déclaration qui équivaut à celle d’une constante. Le mot-clé static précisant que la variable est une variable declasse.

class Personne{private final String nom;...Personne(String n){

nom=n;...

}...

}

Observez que la variable déclarée final doit être initialisée au plus tard par le constructeur. Si ce n’est pas le cas, le compilateurdétectera une erreur.

Le mécanisme d’héritage, dont nous parlerons dans un des chapitres qui suit, va permettre de redéfinir,au sein de sous-classes, des méthodes qui avaient été définies à un niveau supérieur. Dans certains cas,on souhaitera soit empêcher cet héritage, soit empêcher la re-définition de certaines méthodes.

On empêche l’héritage en déclarant la classe final. De la sorte, aucune classe ne peut hériter d’elle,c’est-à-dire en étendre la définition43. On fait de même pour les méthodes qui ne peuvent être réécrites.

Page 55: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

méthodes inaltérables.

44 Si un nombre entier est additionné à un nombre en virgule flottante, le résultat sera converti ennombre à virgule flottante.

Chapitre 4 Quelques compléments utiles - 49 -

Signalons encore que les méthodes d’une classe déclarée final sont automatiquement final mais cen’est pas le cas des champs de la classe.

Les opérateurs

Ils sont assez classiques pour qui a déjà fait un peu de programmation. Les opérations se font, bienentendu, avec une certaine priorité. De plus, les conversions implicites sont nombreuses et il convientde les connaître au risque d’aller au devant de certaines surprises. En voici les principaux. Pour lereste, reportez-vous à la documentation.

Les opérateurs arithmétiques

Pour rappel, il existe six types primitifs numériques: quatre entiers et deux flottants. Par ordre croissantdu nombre d’octets nécessaires, on note le type byte (1), le type short (2), le type int (4), le type long(8) chez les entiers, le type float (4) et le type double (8) chez les nombres en virgule flottante.

Nous avons déjà remarqué que l’utilisation de l’opérateur + dans une expression contenant une chaînede caractères entraînait la conversion des autres informations, quel que soit leur type, en chaînes decaractères, même lorsqu’il s’agit d’objets. Il se passe des choses semblables entre les types primitifsnumériques.

Par exemple, lorsque l’opérateur d’addition (+) est utilisé entre des nombres qui sont du type byte oushort, ceux-ci sont promus au rang de int.

Considérez les déclarations suivantes:

byte b1, b2; short s; int i;

Les expressions b1+3, b1+b2, s+1, s+i fournissent toutes un résultat de type int.

Attention! s++ ne pose pas de problème dans la mesure où l’incrémentation s’applique directement à la variable s qui reste de typeshort.

Si les types long, float ou double interviennent, ils provoquent aussi des conversions implicites44.Notez encore que 2. est de type double alors que 2.f est de type float.

Considérez les déclarations:

byte b1, b2; short s; int i; long l; float f; double d;

Page 56: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 50 -

L’expression s*l fournit un résultat de type long. L’expression l+2. est de type double (car 2. l’est). b1*l*2./f est detype double alors que b1*l*2.f/d est de type float.

Les opérateurs arithmétiques classiques sont l’addition, la soustraction, la multiplication, la division etle reste de la division. Leurs symboles respectifs sont: +, -, *, / et %. La division d’un entier par unentier fournit un résultat entier.

int x=10, y=12;

int z=5*x;

System.out.println(“x : y = “ + x/y);

System.out.println(“z : y = “ + z/y);

L’affichage donnera:

x : y = 0

z : y = 4

Les variables de type char n’échappent pas aux conversions implicites. Lorsqu’elles sont employéesdans des expressions utilisant les opérateurs arithmétiques, leurs valeurs sont converties dans le typeint.

Considérez les déclarations:

char c1=’a’, c2=’d’,c3=90; short s=100;

Les expressions c1+c2, c1*c3, c2-c1, c2*s sont toutes de type int. Leurs valeurs respectives sont 197, 8.730, 3 et10.000. La variable c3 est bien de type char (le caractère dont le code est 90).

Les opérateurs logiques et de comparaison

On retrouve les opérateurs classiques (conjonction, disjonction et négation) auxquels il convientd’ajouter les opérateurs logiques optimisés (à court-circuiter).

Les symboles sont les suivants:

opérateurs classiques optimisés

ET & &&

OU | ||

OU exclusif ^

Page 57: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

45 Ces opérateurs doivent obligatoirement s’appliquer à des variables.

Chapitre 4 Quelques compléments utiles - 51 -

NON ~

Lorsqu’un opérateur optimisé est rencontré, le second opérande n’est pas examiné si ce n’est pasnécessaire.

int i=10, j=50;

if (i>12 && j<100) System.out.println(“Condition remplie”);

Dans ce cas, la première comparaison échouant, la seconde expression n’est pas examinée. Il en est de même dans le cas suivant:

if (i<12 || j<100) System.out.println(“Condition remplie”);

Cette opportunité est intéressante lorsque les opérateurs d’incrémentation sont utilisés.

int i=10, j=50;

if (i>12 & j++<100) System.out.println(“Condition remplie avec j = “+j);

else System.out.println(“Condition non remplie avec j = “+j);

Le texte affiché sera Condition non remplie avec j = 51.

int i=10, j=50;

if (i>12 && j++<100) System.out.println(“Condition remplie avec j = “+j);

else System.out.println(“Condition non remplie avec j = “+j);

Le texte affiché sera Condition remplie avec j = 50.

Les principaux opérateurs relationnels sont l’opérateur d’égalité (==) et de différence (!=) ainsi que lesautres opérateurs classiques de comparaison tels <=, >=, <, >.

Les opérateurs d’incrémentation, de décrémentation et d’affectation élargie

Si vous êtes habitués des langages C et C++, l’existence de ces opérateurs ne devrait pas vous étonneroutre mesure. L’opération qui consiste à ajouter ou retrancher une unité à une variable45 étant trèscourante en programmation, ces opérateurs peuvent s’avérer utiles. On distingue les formes “préfixe”et “suffixe” suivant que l’incrémentation s’effectue en priorité ou non.

Quel est celui des deux messages qui sera imprimé?

int i=5;if ( i++>5) System.out.println(“Le test précède l’incrémentation”);if ( ++i>5) System.out.println(“L’incrémentation précède le test”);

Page 58: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 52 -

C’est le second car lors du premier test, la condition est d’abord évaluée à false avant que i ne soit augmenté d’une unité.

Ces opérateurs peuvent être utilisés dans diverses expressions.

int a = 2, b = 5;int i = a * ++bSystem.out.println(“i vaut “ + i + “.”);

L’affichage donnera i vaut 12.

int i = a * b++ aurait provoqué l’affichage de i vaut 10. tout en observant que la valeur de b, à la fin de l’exécution del’instruction, est également 6.

Les opérateurs arithmétiques peuvent être associés à l’affectation pour une opération dite d’affectationélargie. C’est, en fait, un raccourci d’écriture.

Pour doubler la valeur d’une variable, par exemple, on utilisera l’écriture a *= 2. Elle est équivalente à a = a * 2.

Les principaux opérateurs de cette catégorie sont +=, -=, *=, /= et %=.

L’opérateur conditionnel

Il existe en Java un opérateur ternaire qui permet de préciser une condition, un traitement à effectuersi cette condition est vérifiée et le traitement à effectuer sinon. Les trois opérandes sont séparés parles symboles ? et :.

L’expression a<b ? a : b fournit le minimum de a et b.

min = a<b ? a : b;

Les commentaires

Nous avons déjà évoqué la possibilité de mettre des commentaires dans les programmes Java. Cettenécessité se faisant rapidement sentir, précisons qu’il y a trois manières de placer des commentairesdans du code Java.

Vous connaissez la première qui consiste à utiliser une double barre oblique indiquant au compilateurque tout ce qui suit jusqu’à la fin de la ligne est un commentaire.

Page 59: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 53 -

Point p = new Point(3,8); // Construction du point p

Il est parfois nécessaire d’écrire des commentaires plus abondants et de les répartir sur plusieurs lignes.Dans ce cas, on utilise les séquences /* et */ pour débuter et clôturer le bloc concerné.

/* Ce programme permet de... Copyright... Février 2002 */

Il existe une troisième manière de réaliser des commentaires. Elle est liée à la possibilité d’employerun outil de génération automatique de documentation (javadoc) sous forme de documents HTML. Onutilise dans ce cas les séquences de symboles /** et */.

Les tableaux

Nous avons déjà évoqué la possibilité de définir des tableaux en Java. Examinons cette possibilitédans plus de détails.

Les tableaux sont déclarés en précisant leur nom et le type de données qu’ils sont appelés à contenir.La dimension du tableau est à préciser au moment de sa création et non lors de sa déclaration.

int[] t;

ou

int t[];

sont des déclarations équivalentes. La première est cependant plus parlante (t est un tableau d’entiers).

Tous les données d’un tableau sont évidemment du même type. Chaque élément est accédé en utilisantun index dont la première valeur est 0.

public class Tableau{public static void main(String[] args){

Point[] triplet = new Point[5];for (int i = 0; i<5; i++){

triplet[i] = new Point(i, i*i);System.out.println("t[" + i + "] = " + triplet[i]);

}}

}

Cette application fait appel à la classe Point que nous avions améliorée en ajoutant une re-définition de la méthode toString().

Page 60: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

46 Pour tester un programme ou une application, il est souvent plus simple de fournir les données(qui correspondent généralement à des batteries de tests) via des instructions d’affectation.

47 Nous n’avons pas encore parlé de la manière dont mouraient les objets

48 Nous traiterons des paquetages à la fin du chapitre suivant. Sachez simplement que cespaquetages correspondent à des librairies de classes prédéfinies.

Chapitre 4 Quelques compléments utiles - 54 -

L’affichage donne:

t[0] = (0,0)

t[1] = (1,1)

t[2] = (2,4)

t[3] = (3,9)

t[4] = (4,16)

Dans cet exemple, la variable triplet désigne un tableau de quatre objets de type Point. Ce tableauest ensuite initialisé en utilisant de manière répétitive un des constructeurs de cette classe Point.Comme triplet[i] désigne un objet de type Point, sa concaténation entraîne l’utilisation implicite dela méthode toString() de cette classe qui affiche un objet de type Point en fournissant sa coordonnée.

Nous découvrons aussi une des structures de contrôle des actions bien connue de ceux qui pratiquentla programmation impérative. Nous reviendrons sur la syntaxe de cette structure qui est facile àdeviner.

Entrées et sorties

Nous nous sommes contentés, dans les exemples que nous avons examinés, de “hardcoder46” lesdonnées ou de les fournir en paramètres à la ligne de commande. Pour ce qui est des résultats àproduire, nous nous sommes satisfaits de la méthode println de System.out. Ces choix se justifientpar la nécessité, à mon sens, de se focaliser sur l’essentiel. Maintenant que nous avons eu l’occasiond’illustrer le fonctionnement d’une application à travers la vie (naissance et mort47) d’objets, nouspouvons nous permettre d’indiquer une voie plus agréable pour fournir des données de manièreinteractive.

Pour cela, nous aurons besoin d’une classe qualifiée de classe graphique car elle caractérise une boîtede dialogue (optionpane en anglais) de l’interface graphique de communication de l’utilisateur avecl’application. Cette classe s’appelle JOptionPane et fait partie du paquetage48 de classes appeléjavax.swing. En règle générale, et à quelques rares exceptions près, pour qu’un programme puissedisposer des classes prédéfinies auxquelles il fait appel, il faut utiliser une instruction d’importation despaquetages auxquels elles appartiennent. Cette instruction indique au compilateur quelles classes il doitcharger en mémoire afin que le programme fonctionnen correctement.

Page 61: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

49 Rappelons que la classe System fait partie du paquetage java.lang qui est importéautomatiquement.

Chapitre 4 Quelques compléments utiles - 55 -

import javax.swing.*;

public class EntreeDeDonnees{public static void main(String [] args){

String nom = JOptionPane.showInputDialog("Quel est votre prénom?");System.out.println("Bienvenue au cours Java, " + nom + ".");System.exit(0);

}}

La première instruction du fichier est l’instruction d’importation pour le compilateur.

La méthode showInputDialog est une méthode statique qui prend une information de type Objectcomme paramètre (le message à fournir) et renvoie une information de type String. La documentationdes classes vous permettra d’observer que cette méthode possède plusieurs signatures.

La dernière instruction active la méthode statique exit de la classe System49 qui prend commeparamètre un nombre de type primitif int. La valeur est renvoyée au niveau du shell de commande quipeut l’exploiter pour faire autre chose (enchaîner une application en fonction de cette valeur). La valeur0 indique conventionnellement que le programme s’est terminé correctement.

Signalons encore que la boîte de dialogue simplifiée contient un bouton OK et un bouton Annuler. Unclic sur ce dernier bouton correspond à une absence de référence au retour. Le message affiché seradonc Bienvenue au cours Java, null. Bien entendu, dans le cadre d’une programmation correcte,cet événement doit être récupéré.

Les conversions automatiques et le transtypage

Pour terminer ce chapitre, je vous propose d’aborder une première fois un sujet un peu délicat, celuidu transtypage. Pour introduire le sujet, rappelez-vous que l’emploi de certains opérateurs provoquedes conversions de types, par le fait de la recherche d’un type commun. Lorsque vous voulezadditionner une valeur int à une valeur double, par exemple, la valeur int est d’abord convertie endouble. Il est assez simple d’imaginer le schéma des promotions de types possibles.

Un byte est converti au besoin en short ou en tout ce dont un short peut être converti. Il en est demême pour le short qui est converti en int, pour l’int qui est converti en long, le long en float et lefloat en double.

Il faut mentionner que trois conversions peuvent poser un problème de précision: la conversion d’unint en float et celle d’un long en float ou en double. Ces imprécisions sont dues au nombre d’octetsutilisés pour les différentes représentations.

Page 62: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 56 -

Ajoutons qu’un char peut également être promu en int.

En dehors de ces conversions implicites, le programmeur peut souhaiter l’inverse, à savoir, demanderau programme d’utiliser une information de type double comme une information de type int. Cettedemande ne se fait évidemment pas sans perte d’information.

double x = 2.35, y = 1.56;int z = (int)x // x se mue de double en int et perd sa partie fractionnaire

La démarche est possible tous les types d’objets. Un objet de type plus spécialisé se muant en objetde type moins spécialisé. Elle est connue en anglais sous le nom de casting (mue). Comme il est unpeu tôt pour en parler davantage, l’opération de transtypage sera rediscutée dans le chapitre suivantcar c’est le mécanisme d’héritage nous conduira parfois à l’utiliser.

Exercices

L’apprentissage d’un langage ne peut se faire dans l’ignorance totale des primitives de celangage. Evidemment, avec des langages de la trempe de Java, on peut prétendre que lesprimitives sont extrêmement nombreuses, vu le nombre considérable de classes et de méthodesprédéfinies. On peut aussi apprendre à programmer avec ce langage dans l’ignorance de lamajorité de ces classes. Ce serait du snobisme de se passer d’une classe comme la classe String,par exemple. Nous adopterons donc une position intermédiaire en considérant que laconsultation régulière de la documentation, pour une telle classe (quels en sont les champs, lesméthodes, les constructeurs et leurs signatures) est une occasion de découverte autonome dulangage.

Ex 1 Comment définiriez-vous une classe Cercle? Quels en seraient les champs et les méthodes?Pensez à ce que nous avons déjà défini. Imaginez de réécrire la méthode toString() de la classeObject dont hérite implicitement la classe Cercle.

Ex 2 Ecrivez une application qui crée des objets de type Personne. Chaque personne a au moins unnom et un (ou plusieurs) prénom(s), une adresse et éventuellement un numéro de téléphone. Imaginezdes méthodes d’accès et d’altération uniquement pour les champs qui le nécessitent.

Ex 3 Améliorez l’application précédente en ajoutant à chaque personne un matricule unique etinaltérable.

Ex 4 Réécrivez la méthode toString() de la classe Droite du chapitre précédent pour qu’elle renvoieun système d’équations paramétriques de la droite.

Ex 5 Ecrivez une méthode de la classe Cercle qui détermine si une droite donnée lui est tangente.

Ex 6 Utilisez la classe Point que vous pouvez améliorer en donnant la possibilité de nommer les points.Modifiez la méthode toString pour que la chaîne renvoyée ressemble à “Le point b a pourcoordonnée (3,-2)”. Définissez une classe Segment. Quels seront ses constructeurs? Imaginez une

Page 63: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 57 -

méthode pour obtenir le milieu d’un segment, d’autres pour déplacer l’origine, l’extrémité, pourrenvoyer sa longueur.

Ex 7 Imaginez une méthode toString() pour la classe personne qui renvoie une chaîne semblable àcelle-ci:Vandeput Etienne P F M Gmat 123456

Ex 8 Créez la classe CarteAJouer évoquée dans le chapitre précédent. Chaque instance aura unecouleur et une valeur (exemples: carreau et trois, trèfle et valet,...). Prévoyez un constructeur et desméthodes d’accès. Créez aussi une classe JeuDeCartes qui contienne une information concernant lenombre de cartes du jeu. Une instance de cette classe sera considérée comme le résultat d’un mélangedes cartes. Dans la classe JeuDeCartes un certain nombre de méthodes sont définies pour mélangerle jeu, retourner une carte (fournir celle qui se trouve au-dessus du tas), fournir la x-ième carte du jeu.Définissez ces méthodes, leurs paramètres et les résultats qu’elles produisent. Que serait une donne?Comment la définir?

Ex 9 Inventez une classe Vecteur et imaginez des méthodes pour le calcul des produits scalaires,produits vectoriels, multiplication par un réel, de la norme et une méthode permettant la réalisation del’addition des vecteurs.

Ex 10 On veut réaliser des statistiques sur le lancer d’une paire de dés (on s’intéresse à la somme despoints obtenus). Imaginez une application qui simule le lancer de cette paire de dés et qui dresse untableau des résultats après x lancers.

Exercices de compréhension

Le but de ces exercices est de vous faire percevoir les petites subtilités du langage. Ils doiventvous permettre de vous assurer que vous contrôlez bien les déclarations qui sont faites et lesinstructions qui sont données, de même que vous dominez bien la syntaxe du langage. Lasémantique des programmes proposés est parfois inexistante, mais ce n’est pas le but.

Ex 11 Examinez le programme suivant. Quelles sont les numéros des instructions qui poserontproblème à la compilation?

public class Ex11ch4{12

public static void main(String [] args){34

byte b1 = 5, b2 = 127;5short s1, s2;6int i;7char c = 'b';8b2 += b1;9b2 = b2 + b1;10b1 += 2;11

Page 64: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 58 -

s1 = b1 + 2;1s2 = s1 * 2;2c += 2;3c = c + 2;4i = b1 + b2;5i += 5;6System.out.println(b1);7System.out.println(b2);8System.out.println(s1);9System.out.println(s2);10System.out.println(i);11System.out.println(c);12

}13}14En admettant que vous transformiez ces instructions problématiques en commentaires, quels seront lesrésultats affichés par les instructions qui restantes?

Ex 12 Déterminez les instructions qui posent problèmes dans le programme suivant.

Les noms des variables, des méthodes et des classes sont volontairement dépourvus de sémantique defaçon à mettre l’accent sur les mécanismes.

public class Ex12ch4{

public static void main(String [] args){

Bidon b = new Bidon();long valeur = 100;

b.reagir(valeur);b.agir(valeur);agir(valeur);

}}

class Bidon{

private static final long k = 5;private long champ;

static void agir(long valeur){champ = valeur;

}

void reagir (long valeur){champ = valeur;k = valeur;

}}

Page 65: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 59 -

Ex 13 Que va faire afficher le programme suivant, compte tenu des accès possibles et de la résolutionde la surcharge?

Cet exercice, tiré de C. DELANNOY Exercices en Java Eyrolles Paris 2001 demande une bonneconnaissance des principes de résolution de la surcharge. Son énoncé a été repris pratiquement commetel.

public class Ex13ch4{

public static void main(String [] args){

A a = new A();a.g();

System.out.println("A partir de la méthode MAIN");System.out.println("---------------------------");

int n =1;long q = 12;float x =1.5f;double y = 2.5;

a.f(n,q);a.f(q,n);a.f(n,x);a.f(n,y);

}}

class A{public void f(int n, float x){

System.out.println("f(int n, float x)\tn = " + n + " x = " + x);}

private void f(long q, double y){System.out.println("f(long q, double y)\tq = " + q + " y = " + y);

}

public void f(double y1, double y2){System.out.println("f(double y1, double y2)\ty1 = " + y1 + " y2 = "+ y2);

}

public void g(){int n =1;long q = 12;float x =1.5f;double y = 2.5;System.out.println("A partir de la méthode g");System.out.println("------------------------");f(n,q);f(q,n);

Page 66: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 4 Quelques compléments utiles - 60 -

f(n,x);f(n,y);

}}

Page 67: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 61 -

Chapitre 5 Java: héritage et polymorphisme

Héritage

Spécialisation et généralisation

Lorsqu’on examine le domaine d’une application, on se rend vite compte que des classes d’objetsapparaissent. L’appartenance d’un objet à une classe est liée au fait que cet objet possède un état (deschamps) et un comportement (des méthodes) qui sont communs à tous les éléments de la classe.

Il arrive cependant que plusieurs de ces objets possèdent un état et/ou un comportement que l’onsouhaiterait définir plus finement que les autres. En d’autres termes• les caractéristiques de l’état demeurent mais sont éventuellement plus nombreuses (davantage de

champs);• le comportement change ou se développe (autre implémentation de certaines méthodes et/ou

méthodes supplémentaires).

Cet état de chose se caractérise par le fait que, dans la description du système d’information analysé,des relations de type “est un...” ou “est une...” apparaissent.

Un carré est un quadrilatère, un directeur d’école est un enseignant,...

Jusqu’à présent, nous nous sommes limités aux relations de type “a un...” ou “a une...” qui donnaientlieu à de la composition.

Une droite a une pente, un cercle a un centre,...

La meilleure façon de répondre aux relations “est un...” est d’introduire la notion d’héritage. La notiond’héritage doit être comprise dans le sens où une classe qui hérite d’une autre classe la spécialise. Ladescription de la classe héritée est donc en principe plus fine que la description de la classe parente.

Le directeur a une date d’engagement comme directeur qui est une autre information que sa date d’engagement commeenseignant.

De la même manière, il est possible que plusieurs classes d’objets apparaissent à un moment donnécomme faisant partie d’une classe plus générale. On peut alors décider, a posteriori, que ces classeshéritent de cette classe plus générale. Cette démarche porte le nom de généralisation.

Page 68: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

50 Bien qu’il s’agisse d’une nouvelle écriture d’une méthode, il existe une nuance entre leremplacement et la surcharge. Elle est expliquée quelques paragraphes plus en avant. Enanglais, les mots utilisés sont respectivement overriding et overloading.

Chapitre 5 Java: héritage et polymorphisme - 62 -

Forme fermée Forme ouverte

Forme

Quadrilatère

Carré

Après avoir défini des carrés, des cercles, des polygones irréguliers,... on peut souhaiter définir uneclasse plus générale de formes fermées qui, avec une autre classe de formes ouvertes, pourrait à sontour hériter d’une classe encore plus générale de formes.

Le schéma se lit de la manière suivante:• un carré est un quadrilatère;• un quadrilatère est une forme fermée;• une forme fermée est une forme;• une forme ouverte est une forme.

Le schéma n’est pas nécessairement exhaustif et peut êtrecomplété vers le bas ou vers le haut. C’est également undes avantages de la POO que de pouvoir traiter de manièreprogressive des parties de problèmes.

La classe dont hérite une classe est appelée sa superclasse.

Intérêt

L’intérêt d’introduire le mécanisme d’héritage dans la programmation est évident. Le programmeurpeut se contenter de spécialiser des classes assez générales plutôt que de tout inventer. La réutilisabilitélogicielle est manifeste. Un autre avantage réside dans le polymorphisme des objets. Nous en parlonsplus loin.

Héritage, surcharge et remplacement

Ne confondez pas surcharge et remplacement. La surcharge existe indépendamment de la notiond’héritage. On peut dire d’une méthode qu’elle est surchargée lorsqu’elle possède plusieurs signaturesdans la définition de la classe. Le mécanisme d’héritage vient enrichir cette possibilité de surcharge.Une méthode définie dans la superclasse peut être surchargée dans la classe qui en hérite. Il faut pourcela que la nouvelle signature soit différente de toutes les signatures de la méthode dans la superclasse.Si elle est identique à une des signatures de la superclasse, on parle alors de remplacement plutôt quede surcharge.

Syntaxe en Java

Voyons comment traduire le phénomène de l’héritage en Java. On dira d’une classe qui hérite d’uneautre classe, qu’elle étend cette dernière. L’extension concerne évidemment la possibilité d’ajouter deschamps mais aussi des méthodes ou de surcharger, voire remplacer50 ces dernières.

La représentation schématique de l’extension est une flèche dont la pointe est un triangle creux etdirigée de la classe qui étend vers celle qui est étendue.

Page 69: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 63 -

Le mot réservé du langage est le mot extends .

class Carre extends Quadrilatere{

...

}

Grâce à l’héritage, il n’est pas nécessaire de redéfinir les champs et les méthodes qui sont héritées.Toutefois, le programmeur garde la possibilité de modifier la description des méthodes.

Il peut aussi enrichir la définition de la classe héritée en ajoutant des champs, en ajoutant des méthodesou en surchargeant les méthodes héritées (c-à-d. en en écrivant de nouvelles, portant le même nom,avec un nombre ou des types différents de paramètres).

Le mécanisme d’héritage est somme toute assez simple à comprendre. La richesse de ce qu’il proposevous obligera cependant à être très attentifs à une foule de petit détails qui risquent, au début, de vousfaire découvrir l’univers implacable des erreurs de compilation.

Voici un exemple qui met en évidence les caractéristiques principales de l’héritage et ses liaisons avecla surcharge et le remplacement.

/* Illustration de l'héritage, de la surcharge et du remplacement */

public class AppHSR{/** Classe d’application */

public static void main(String [] args){/** Création de trois objets: */

Eleve e = new Eleve("Vandeput", "", "5TTr");Personne p1 = new Personne("Vandeput");Personne p2 = new Personne("Vandeput", "Etienne");

// Impression des objets comme chaînes de caractèresSystem.out.println(e + "\n\n" + p1 + "\n\n" + p2 + "\n");

}}

class Personne{

protected final String nom;protected final String prenom;protected Residence r; //------------------------>Composition

public Personne(String nom){this.nom = nom;this.prenom = "";

}

public Personne(String nom, String prenom){ //--->Surcharge

Page 70: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 64 -

this.nom = nom;this.prenom = prenom;

}

public String toString(){return "Identité: " + nom + " " + prenom;

}}

class Eleve extends Personne{

private String classe;

public Eleve(String nom, String prenom, String c){super(nom, prenom);classe = c;

}

public String toString(){ //--------------------->Remplacementreturn "Identité " + nom + " " + prenom + "\nClasse: " +classe;

}}

class Residence{

private String rue;private String numero;private int codePostal;private String localite;

}

La méthode main de l’application crée trois objets: deux de type Personne et un de type Eleve.

La classe Personne possède trois champs de données dont un est de type Residence. Ceci illustre leprincipe de composition (relation “a un...” ou “a une...”). Une Personne a une Residence. La classeResidence est écrite pour éviter les erreurs à la compilation mais aucun objet de type Residence n’estinstancié dans le programme.

La classe Personne possède deux constructeurs: l’un sur base d’une chaîne qui est le nom, l’autre surbase de deux chaînes que sont le nom et le prénom. Cette classe qui hérite implicitement de la classeObject, redéfinit (remplacement) la méthode toString de cette dernière.

La classe Eleve hérite de la classe Personne. Elle possède un seul constructeur sur base des nom,prénom et classe de l’élève. Il est à noter que ce constructeur fait appel au constructeur de lasuperclasse pour ce qui est d’initialiser les champs nom et prenom. Lorsqu’il est nécessaire, l’appelau constructeur de la superclasse doit être la première instruction du constructeur. Cet appel

Page 71: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

51 Attention, super n’est pas comme this une référence à un objet mais une mot-clé spécial quipermet d’invoquer une méthode ou un constructeur de la superclasse.

52 Notez que si une classe ne possède aucun constructeur dans sa définition, elle possède toutde même un constructeur par défaut qui est fatalement sans arguments.

Chapitre 5 Java: héritage et polymorphisme - 65 -

est réalisé au moyen du mot clé super51. Le constructeur de la superclasse reçoit deux des troisarguments reçus par le constructeur de la classe.

Le modificateur d’accès “protected”

Tout cela ne se fait pas sans observer que la méthode toString doit accéder aux champs de lasuperclasse. Cet accès ne lui est pas autorisé si ces champs sont déclarés private. C’est pourquoi unnouveau modificateur d’accès est nécessaire. Le modificateur protected appliqué aux champs (ou auxméthodes) d’une classe permet aux méthodes de ses sous-classes et de toutes les classes qui en héritentdirectement ou indirectement d’accéder à ces champs et à ces méthodes.

La méthode toString de la classe Eleve remplace la méthode correspondante de la classe Personnequi aurait été héritée sinon.

Constructeurs et héritage

Lorsque le constructeur d’une classe est invoqué, c’est d’abord le constructeur sans argument de lasuperclasse qui est invoqué avant que les instructions du constructeur de la classe ne soient exécutées.Si ce constructeur sans argument n’existe pas, il est impératif d’en appeler un explicitement en utilisantle mot-clé super. Il est également possible d’appeler un autre constructeur de la classe plutôt que des’adresser aux constructeurs de la superclasse.

Voici, pour illustrer, un petit programme qui construit un objet de type A et un autre de type B héritédu type A. La classe A possède un constructeur sans arguments52 et pourrait d’ailleurs en posséderd’autres.

public class ConstrHerit{public static void main(String [] args){

A a = new A();B b = new B(2);System.out.println(a + "\n" + b);

}}

class A{protected int x;

A(){System.out.println("Un objet A est construit.");

}public String toString(){

return "x: " + x;

Page 72: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 66 -

}}

class B extends A{private int y;

B(int y){this.y = y;

}public String toString(){

return "x: " + x + "\t" + "y: " + y;}

}

Observez que la construction de l’objet de type B provoque un nouvel affichage de la phrase: “Unobjet A est construit”, preuve, si nécessaire, que le constructeur sans argument de A est invoqué.Dans ce cas précis, la suppression du constructeur sans argument de A ne poserait pas de problème.En effet, l’absence de toute définition de constructeur pour A provoque l’appel du constructeur pardéfaut. En revanche, si au moins un constructeur de A avec argument(s) existe, l’absence deconstructeur sans argument provoque une erreur de compilation.

Il y a toutefois d’autres manières de procéder. Il est possible d’appeler un constructeur de lasuperclasse avec arguments (mot-clé super) ou un autre constructeur de la même classe (this).

Voici une autre version du programme précédent qui fonctionne en l’absence d’un constructeur sansargument dans A. Les changements sont en caractères gras.

public class ConstrHerit2{public static void main(String [] args){

A a = new A(5);B b = new B(2,4);System.out.println(a + "\n" + b);

}}

class A{protected int x;

A(int x){this.x = x;

}public String toString(){

return "x: " + x;}

}

class B extends A{private int y;

B(int x, int y){

Page 73: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 67 -

super(x); // Appel explicite d’un constructeur de Athis.y = y;

}public String toString(){

return "x: " + x + "\t" + "y: " + y;}

}

Polymorphisme

Grâce au mécanisme d’héritage, le programmeur peut s’adresser à un objet par l’intermédiaire d’uneclasse parente. Il ne doit donc pas se préoccuper du type réel de l’objet et de la méthode à mettre enoeuvre.

Si la classe Carre hérite de la classe Quadrilatere qui elle-même hérite de la classe Forme et qu’uneméthode dessineToi est définie au niveau de cette dernière, le programmeur peut référencer un objetCarre comme étant une Forme et lui demander de se dessiner.

class Forme{...public void dessineToi(){

...}

}

class Carre extends Quadrilatere}int cote;public Carre(int c){

cote = c;}...

}

class Quadrilatere extends Forme{...

}

public class ApplHeritage{public static void main(String [] args){

...Forme c = new Carre(10);...c.dessineToi();...

}}

Page 74: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

53 Dans la réalité, l’implantation complète d’une classe comme la classe Forme est difficile. Comment, en effet, décrire une méthode comme dessineToi? Néanmoins, une telle classe a sonutilité dès lors qu’il est possible, comme le montre l’exemple, de s’adresser à des objetsdifférents en considérant que ce sont tous des formes. On s’en sort en qualifiant une telleclasse de classe abstraite, c-à-d. dont une partie au moins des méthodes n’est pas implantéemais seulement déclarée. Nous traitons des classes abstraites et des interfaces dans un deschapitres qui suivent.

Chapitre 5 Java: héritage et polymorphisme - 68 -

Intérêt

Le polymorphisme permet au développeur de s’occuper des généralités en laissant l’environnementd’exécution s’occuper des spécificités. Grâce à lui, un appel à une méthode provoque des actionsdifférentes selon le type des objets concernés.

Forme c = new Carre(10);Forme r = new Rectangle(12,20);/*En supposant qu’il existe une classe Rectangle et un constructeur adéquat*/...c.dessineToi();r.dessine-toi();...

La même méthode appliquée à des objets Formes référençant des objets de sous-classes différentesvont produire des actions différentes53. La technique de dessin d’un carré ne sera pas identique à celled’un rectangle. En réalité, le programmeur demande aux objets de se dessiner sans avoir à sepréoccuper de la manière dont cela se fera.

Mécanisme

Compte tenu de ce qui vient d’être dit, il faut être vigilant quant à la manière dont les méthodes sontchoisies, car des choses se passent à la compilation et d’autres choses se passent à l’interprétation.Ceci nous amène à expliquer qu’un programme utilise soit la liaison statique , soit la liaisondynamique pour appeler une méthode.

Si une méthode est déclarée static (ou private, ou final) le compilateur sait que c’est bien elle qui seraexécutée en cas d’appel. La résolution de la surcharge se limite aux méthodes de la classe et le choix,basé sur le nombre et le type de paramètres, permet de connaître immédiatement la méthode àappliquer comme dans l’exemple suivant.

public class ResSurSta{

public static void main(String [] args){System.out.println(Essai.auCarre(9) + "\t" +Essai.auCarre(3,4));

}

Page 75: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

54 Enfin, vous me comprenez!

Chapitre 5 Java: héritage et polymorphisme - 69 -

}

class Essai{

public static int auCarre(int x){return x*x;

}

public static int auCarre(int x, int y){return x*x + y*y;

}}

En revanche, lorsque ce n’est pas le cas (méthode public et aucun des modificateurs final et static),le compilateur recherche la meilleure des méthodes (résolution de la surcharge) en tenant compte dutype déclaré de l’objet invoqué .

A l’issue de cette résolution, le compilateur connaît la signature de la méthode à appliquer. Ce n’estpas suffisant. Lors de l’exécution, il est possible que l’objet déclaré de type A soit en fait du type B(hérité de A).

Un Directeur est un Enseignant. Dès lors, des instructions comme celles qui suivent ont du sens...

Enseignant e = new Directeur(“Duchâteau”);Forme f = new Carre(5);Quadrilatere q = new rectangle(12,20);

...pour autant que les classes Carre et Rectangle héritent de la classe Quadrilatère et que cette dernière hérite de la classe Forme,par exemple.

Ainsi, un objet f déclaré de type Forme, peut être en réalité un Carre.

En revanche, il en est d’autres qui sont dangereuses et qui sont à proscrire, telle:

Directeur d = new Enseignant(“Duchâteau”);

Si un Directeur est aussi un Enseignant, l’inverse n’est pas vrai. On pourra demander à un objet ede type Enseignant, tout ce qu’on peut demander à ce type d’objet. Si l’objet réel est un Directeur,il n’y a pas de problème puisque le Directeur hérite du comportement de l’Enseignant.

On ne pourra demander à un objet de type Enseignant de se comporter totalement comme un objetde type Directeur puisque le comportement de ce dernier a certainement été enrichi54 ou modifié.

Page 76: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 70 -

Dans l’exemple qui suit, la première construction est correcte, la seconde entraîne une erreur decompilation.

public class Poly1{

public static void main(String [] args){A a = new B();B b = new A();

}}

class A{

...}

class B extends A{

...}

Mais revenons au problème de la résolution de la surcharge. Le compilateur a déterminé la signaturede la méthode à utiliser lors de l’appel à un objet o de type O. A l’exécution, l’interpréteur identifie letype réel de l’objet (O ou une de ses sous-classes, soit O1). Il recherche d’abord dans O1 , uneméthode qui a la signature déterminée par le compilateur. S’il la trouve, c’est elle qu’il utilise, sinon,il cherche en remontant dans la hiérarchie.

Voilà tout le mécanisme du polymorphisme expliqué.

En résumé:

• le compilateur détermine la signature de la méthode sur base du type déclaré de l’objet;

• l’interpréteur recherche la méthode qui porte cette signature dans la classe correspondant au typeréel de l’objet au moment de l’exécution et s’il ne la trouve pas, il la recherche en remontant dansla hiérarchie.

Exercices

Ex 1 Le programme suivant sera-t-il compilé? Si non dites pourquoi, si oui, précisez quels seront lesrésultats affichés.

public class Ex1ch5{

public static void main(String [] args){

A a1 = new A();A b1 = new B();

Page 77: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

55 C’est ce qui se passe lorsqu’aucun constructeur de la superclasse (avec super) ou de la mêmeclasse (avec this) n’est invoqué explicitement.

Chapitre 5 Java: héritage et polymorphisme - 71 -

A b2 = new B(5);A a2 = new B(-1);

System.out.println(a1.valeur);System.out.println(b1.valeur);System.out.println(b2.valeur);System.out.println(a2.valeur);

}}

class A{

int valeur;}

class B extends A{

B(){valeur=100;

}

B(int v){valeur = valeur + v;

}}

L’initialisation des variables b1, b2 et a2 sont correctes. Un objet de type A pourra assurer lecomportement des objets de ce type s’il est réellement un objet de type B plus spécialisé que le typeA.

La définition de la classe A ne comprend pas de définition de constructeur. C’est donc le constructeurpar défaut qui est utilisé. Celui-ci attribuera la valeur par défaut au champ valeur, c-à-d. 0.

Deux constructeurs garnissent la définition de la classe B. L’initialisation de b1 utilise le premier quiaffecte 100 à la variable valeur. L’initialisation de b2 utilise le second qui invoque nécessairement leconstructeur par défaut de la superclasse55. La variable valeur contient donc d’abord 0 avant d’êtreaugmentée de la valeur de v par le constructeur de B. Finalement, le champ valeur contiendra 5.

L’initialisation de a2 passe par la construction d’un objet de type B. Le processus est donc pareil àcelui de la construction précédente. Le champ valeur contiendra -1.

Ex 2 Cet exercice n’est pas trop facile. Les noms des classes, des champs et méthode sontvolontairement dépourvus de sémantique.

Voici les définitions de deux classes.

Page 78: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 72 -

class A{

A autre;

void methode(A v){autre = new A();

}}

class B extends A{

int valeur

void methode(A v){super.methode(v);valeur++;

}

void methode(B v){autre = v;

}}

Parmi les instructions qui suivent, quelles sont celles qui ont du sens. Si elles n’en ont pas, ditespourquoi. Décrivez ce qui se passe effectivement dans le cas où les instructions sont valables.

1. A x = new A();A y = new A();x.methode(y);

2. A x = new A();A y = new B();x.methode(y);

3. A x = new A();B y = new B();x.methode(y);

4. B x = new B();B y = new B();x.methode(y);

5. A x = new B();B y = new B();x.methode(y);

6. B x = new B();B y = new A();x.methode(y);

Ex 3 Considérez la hiérarchie suivante:

Page 79: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 73 -

les classes Carre, Rectangle et Parallelogramme héritent de la classe Quadrilatere qui, avec laclasse Triangle hérite de la classe Polygone, elle-même héritant de la classe Forme.

Que va imprimer le programme qui suit?

public class Hierarchie1{

public static void main(String [] args){

Parallelogramme p = new Parallelogramme();Rectangle r = new Rectangle();Carre c = new Carre();Quadrilatere q = new Quadrilatere();Triangle t = new Triangle();Polygone g = new Polygone();Forme f = new Forme();

p.seDefinir();r.seDefinir();c.seDefinir();q.seDefinir();t.seDefinir();g.seDefinir();f.seDefinir();System.out.println();

f = c;g = r;q = p;

f.seDefinir();g.seDefinir();q.seDefinir();

}}

class Forme{

public void seDefinir(){System.out.println("Je suis une forme.");

}}

class Polygone extends Forme{

public void seDefinir(){super.seDefinir();System.out.println("Je suis aussi un polygone.");

}}

class Triangle extends Polygone{}

Page 80: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 74 -

class Quadrilatere extends Polygone{}

class Carre extends Quadrilatere{public void seDefinir(){

System.out.println("Je suis un carré.");}

}

class Rectangle extends Quadrilatere{public void seDefinir(){

super.seDefinir();System.out.println("Je suis aussi un rectangle.");

}}

class Parallelogramme extends Quadrilatere{}

A peu de choses près, la seule méthode invoquée dans la méthode main s’appelle seDefinir et nepossède pas de paramètre. Il est clair que, quel que soit le type d’objet déclaré sur lequel cetteméthode est invoquée, la signature de la méthode à appliquer est, dans tous les cas, seDefinir().

A l’exécution, l’interpréteur va rechercher cette méthode dans la classe réelle de l’objet. Cela donne:

Invocation Output

p.seDefinir(); Je suis une forme.Je suis aussi un polygone.

r.seDefinir(); Je suis une forme.Je suis aussi un polygone.Je suis aussi un rectangle.

c.seDefinir(); Je suis un carré.

q.seDefinir(); Je suis une forme.Je suis aussi un polygone.

t.seDefinir(); Je suis une forme.Je suis aussi un polygone.

g.seDefinir(); Je suis une forme.Je suis aussi un polygone.

f.seDefinir(); Je suis une forme.

Après les nouvelles affectations

f.seDefinir(); Je suis un carré.

Page 81: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 75 -

g.seDefinir(); Je suis une forme.Je suis aussi un polygone.Je suis aussi un rectangle.

q.seDefinir(); Je suis une forme.Je suis aussi un polygone.

Lorsque la classe ne possède pas de méthode seDefinir, l’interpréteur examine la superclasse. C’estle cas pour l’objet q, par exemple. Il faut aller voir dans la classe Polygone.

Dès qu’il trouve une méthode, il l’exécute. Cette méthode peut éventuellement faire appel à uneméthode de la superclasse. C’est le cas pour la méthode seDefinir de la classe Polygone qui fait appelà la méthode seDefinir de sa superclasse Forme. Dans ce cas, il y a d’abord impression du texte “Jesuis une forme.” avant l’impression du texte “Je suis aussi un polygone.”.

Ex 4 Cet exercice illustre le double processus de détermination, à la compilation, de la signature de laméthode qu’il faudra rechercher et de recherche, à l’interprétation, d’une méthode portant cettesignature à partir de la classe correspondant au type réel et éventuellement dans les classes dont ellehérite.

Que va afficher le programme suivant:

public class Poly2{public static void main(String [] args){

A a = new A();A b = new B();A c1 = new C();C c2 = new C();B d1 = new D();A d2 = new D();

a.m("Hello",5);a.m(5,"Hello");a.m(5,10);a.m("5",10);b.m(5,"Hello");b.m(5,10);c1.m(5,10);c1.m(5,"Hello");c2.m(5,10);d1.m(5,"Hello");d2.m("Hello",5);d2.m(5,10.f);d2.m(5.f,10);

}}

class A{void m(String s, int i){

Page 82: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 76 -

System.out.println("Dans A: m(String , int)\t" + s + "\t" + i);}void m(int i, String s){

System.out.println("Dans A: m(int, String)\t" + i + "\t" + s);}void m(int i1, int i2){

System.out.println("Dans A: m(int, int)\t" + i1 + "\t" + i2);}

void m(float f1, float f2){System.out.println("Dans A: m(float, float)\t" + f1 + "\t" + f2);

}}

class B extends A{

void m(int i, String s){System.out.println("Dans B: m(int, String)\t" + i + "\t" + s);

}

void m(float f, int i){System.out.println("Dans B: m(float, int)\t" + f + "\t" + i);

}}

class C extends A{

void m(int i1, int i2){System.out.println("Dans C: m(int, int)\t" + i1 + "\t" + i2);

}}

class D extends B{

void m(int i1, int i2){System.out.println("Dans D: m(int, int)\t" + i1 + "\t" + i2);

}

void m(int i, String s){System.out.println("Dans D: m(int, String)\t" + i + "\t" + s);

}}

Seule la méthode m imprime des choses. Bien qu’elle demande toujours deux paramètres, elle possèdeun certain nombre de signatures.

Le diagramme d’héritage est assez simple:• D hérite de B qui hérite de A• C hérite de A

Page 83: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 5 Java: héritage et polymorphisme - 77 -

Dans les définitions des classes, la définition de la méthode m vient parfois surcharger les méthodesexistantes ( m(float f, int i) dans B, par exemple) et parfois les remplacer (m(inti1,int i2) dans C, par exemple).

Page 84: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

56 Ce chapitre ne traite pas, par exemple, de l’opportunité d’utiliser telle ou telle structure danstelles ou telles circonstances, ni de la manière, plus ou moins raisonnable, de créer cesstructures. Vous pouvez vous reporter pour cela aux publications du CeFIS traitantd’algorithmique.

57 Lorsque le bloc ne comprend qu’une seule instruction, les accolades ne sont évidemment pasnécessaires.

Chapitre 6 Les structures de contrôle - 78 -

Chapitre 6 Les structures de contrôle

Utilité

Les structures de contrôle existent dans de nombreux langages. Les langages OO n’y échappent pasen ce sens que la création d’objets et la communication entre ces derniers n’exclut pas la possibilitéd’écrire des algorithmes. Nous nous contenterons ici de les énoncer, en vrac et pour information,sachant que les lecteurs de ces notes ont connaissance certaines généralités concernant ces structures56.

Les structures alternatives

Alternative simple

On retrouve en Java, comme dans de nombreux langages, le bon vieux “si... alors...sinon...” Sasyntaxe est la suivante:

if(expression_booléenne) {bloc_de_traitement1

}else{

bloc_de_traitement2}

Les remarques principales concernent la condition qui est évidemment une expression booléenne devantse trouver entre parenthèses, le caractère optionnel de la partie else et le fait que les traitements àeffectuer dans l’un ou dans l’autre cas sont regroupées dans un bloc57.

if (p1.equals(p2)){System.out.println("Ces deux points ne définissent pas une droite!");

}else{

Droite d = new Droite(p1,p2);System.out.println("La droite d a pour équation: ", d);

}

Page 85: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 6 Les structures de contrôle - 79 -

L’écriture de la condition peut faire intervenir des variables de types primitifs, des objets, desinvocations de méthode et, bien entendu, des opérateurs en tous genres.

Alternative multiple

Il convient de faire une remarque à propos de cette structure. Elle concerne la manière dont l’analysed’un problème logiciel est menée. Il est clair que les observations faites dans le chapitre précédentconcernant l’héritage et surtout le polymorphisme, pousseront souvent les développeurs d’applicationsà envisager une hiérarchie de classes d’objets avec une spécialisation des méthodes au niveau des sous-classes. Dès lors, le polymorphisme aidant, la structure dont il est question ici s’avère inutile. A unchoix multiple, se substitue la résolution de la surcharge et la recherche de la méthode possédant unesignature convenable. Toutefois, la nécessité d’employer une structure avec alternative multiple n’exigepas toujours le passage par une hiérarchie de classes. C’est au programmeur d’en juger.

La syntaxe de cette structure est la suivante:

switch (valeur_entière){

case valeur1:bloc_de_traitement1

case valeur2:bloc_de_traitement2

...

default:bloc_de_traitement_par_défaut

}

Quelques remarques importantes sont à faire:

• le paramètre à fournir à la structure switch doit correspondre à une expression ayant une valeurentière;

• si la valeur de cette expression ne se trouve pas dans les valeurs mentionnées, c’est le traitementpar défaut qui est réalisé;

• s’il n’y a pas de traitement par défaut et que la valeur n’est pas rencontrée, la structure estsimplement ignorée;

• la plupart des blocs de traitement doivent se terminer par l’instruction break; qui assure que lesautres possibilités ne sont pas examinées;

• une instruction return équivaut à l’utilisation d’une instruction break.

Page 86: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

58 Il vous est loisible d’améliorer la classe Clavier en y ajoutant une méthode lireChar().

Chapitre 6 Les structures de contrôle - 80 -

Voici, à titre d’illustration, un petit programme qui affiche la valeur décimale d’un caractère (supposéêtre) hexadécimal. Le programme saisit le caractère sous forme d’une chaîne58 et affiche un messages’il n’y a pas ou plus d’un caractère.

public class HexaVersDeci{

public static void main(String [] args){

String car = Clavier.lireString();

if (car.length()!=1){System.out.println("ERREUR: un seul caractère");

}else{

char c=car.charAt(0);int val=deci(c);if(val!=-1){

System.out.println("Valeur du caractère:" + val);}

}}

public static int deci(char c){

switch(c){

case '0': case '1': case '2': case '3': case '4':case '5': case '6': case '7': case '8': case '9':

return c-'0';

case 'A': case 'B': case 'C': case 'D': case 'E':case 'F':

return c-'A'+10;

case 'a': case 'b': case 'c': case 'd': case 'e':case 'f':

return c-'a'+10;

default:System.out.println("Ce caractère n’est pas employé en\hexadécimal");return (-1);

}}

}

La méthode main n’affiche la valeur du caractère que si celui-ci est valide. La chaîne (même si elle necomprend qu’un seul caractère) doit être transformée en caractère par les vertus de la méthode charAt.

Page 87: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

59 Ce nombre peut être déterminé par la connaissance d’une valeur initiale et d’une valeur finale.

60 Il faut espérer que le programmeur ne s’est pas arrangé pour que n tende vers l’infini.

Chapitre 6 Les structures de contrôle - 81 -

Dans la structure switch, les cas semblables sont regroupés. Les opérations arithmétiques entrecaractères donnent bien des entiers de type int (promotion). Les instructions return ont aussi l’effetde l’instruction break.

Vous pourrez modifier sans trop de peine la méthode main pour que le programme fonctionnelorsqu’une chaîne de plus d’un caractère est fournie. Il y a lieu, dans ce cas, de distinguer les messages:• “Vous n’avez pas fourni de caractère”• “Vous avez fourni plus d’un caractère. Seul le premier a été pris en compte.”

Les structures répétitives

On retrouve ici les trois structures classiques permettant d’effectuer un traitement tant qu’unecondition est vérifiée, jusqu’à ce qu’une condition soit vérifiée ou encore, un nombre connu59

de fois.

La boucle “while”

Les instructions contenues dans une telle boucle sont exécutées de 0 à n fois60. La valeur booléennede la condition peut en effet être false dès le départ ce qui fait que les instructions de la boucle sontignorées. Voici la syntaxe:

while (expression_booléenne){bloc_de_traitement

}

Un (très) petit exemple:

public class BoucleWhile{

public static void main(String [] args){

int i = 0;while (i++<8){

System.out.println("Bienvenue à la séance " + i +" du cours Java.");

}}

}

Les accolades ne sont pas nécessaires si le bloc ne contient qu’une seule instruction.

Page 88: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 6 Les structures de contrôle - 82 -

La boucle “do - while”

Le bloc d’instructions est exécuté au moins une fois puisque la condition n’est testée qu’en fin deboucle.

dobloc_de_traitement

while (expression_booléenne);

Vous remarquerez que les instructions à l’intérieur d’une structure do - while constituent toujours unbloc. De même, l’expression while(expression_booléenne) est obligatoirement suivie d’un point-virgule.

Un exemple assez semblable au précédent:

public class BoucleDoWhile{

public static void main(String [] args){

int i = 1;do

System.out.println("Bienvenue à la séance " + i +" du cours Java.");

while (i++<8);}

}

Notez encore que l’expression booléenne pouvait s’écrire ++i<=8.

La boucle “for”

Dans cette structure, sont à préciser, les valeurs initiale(s) et finale(s) de la (des) variable(s) à prendreen considération. En voici la syntaxe:

for (initialisations; expression_booléenne; incrémentations){bloc_de_traitement

{

Et toujours le même exemple...

public class BoucleFor{

public static void main(String [] args){

for(int i=1;i<=8;++i){System.out.println("Bienvenue à la séance " + i +" du cours Java.");

}}

Page 89: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 6 Les structures de contrôle - 83 -

}Comme le laisse supposer la syntaxe, il est possible d’initialiser plusieurs variables et de préciserplusieurs incrémentations (au sens large). La méthode qui suit renvoie l’exposant de la plus grandepuissance de 2 supérieure ou égale à un nombre donné (en paramètre).

public static int puissanceDeDeux(int valeur){

int exp;for(exp=0, valeur-=1; valeur>0; exp++, valeur/=2){}return exp;}

}

Quelques commentaires sont nécessaires. A chaque passage dans la boucle, il faudra diviser par deux(division entière) et augmenter de 1 la valeur de l’exposant. Cette dernière devra être initialisée à 0 etla valeur à diviser, à la valeur fournie moins une unité pour assurer qu’il s’agit bien d’un nombresupérieur ou égal (si valeur vaut 2, il faudra deux divisions avant que v vaille 0, alors que l’exposantdoit valoir 1).

Il y a donc deux instructions d’initialisation et deux instructions d’incrémentation (augmenter de 1,diviser par 2).

Il n’y a pas d’instruction dans le bloc. Ce n’est pas nécessaire puisqu’elles sont incluses dans lastructure for. En revanche, l’absence d’accolades entraîne une erreur de compilation.

La variable exp ne peut être initialisée dans la boucle car elle est valeur de retour.

“break”, “label”, “continue” et autre “return”

De nombreux discours tentent, sans doute à raison, de pousser les programmeurs dans la voie d’uneprogrammation structurée. Il arrive cependant parfois que l’on confonde recommandation et impératif.Lorsque plusieurs structures sont imbriquées, on souhaite parfois échapper à l’imbrication dans des casspécifiques. Sans pour cela en faire une habitude, l’emploi des instructions qui suivent peut parfoiss’avérer intéressant.

L’instruction break ne permet pas seulement de quitter une structure switch comme vu précédemment.Elle permet de quitter le bloc dans lequel elle est écrite. Elle interrompt ainsi le flux d’exécution.

Interruption classique:

doint revenus = Clavier.lireInt() ;if(revenus<100000)

break;...

while(...);

Page 90: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 6 Les structures de contrôle - 84 -

L’instruction peut être utilisée dans l’ensemble des structures qui viennent d’être présentées: if -else,switch, while, do - while, for.

Lorsque plusieurs blocs sont imbriqués, il est possible de quitter un bloc plus externe que le bloc danslequel se trouve l’instruction. Il suffit alors d’étiqueter ce bloc et d’utiliser cette étiquette à la suite del’instruction break. L’étiquetage d’une instruction se fait de la manière suivante:

étiquette: instruction

Interruption labélisée:

taxation:do

int revenus = Clavier.lireInt() ;if(revenus<100000){

System.out.println("Pas de taxation");break taxation;

}...

while(...);

L’instruction continue dans une boucle permet également de modifier le flux d’exécution en renvoyantl’exécution à la fin de la boucle et donc à l’évaluation de la condition de poursuite ou d’arrêt.

Enfin, vous connaissez déjà l’instruction return utilisée pour renvoyer une valeur ou une référenced’objet à un appelant. Lorsqu’il n’y a pas de valeur de retour, l’instruction rend tout simplement la mainà l’appelant, ce qui est également une façon de rompre le flux normal d’exécution du programme.

Exercices

Ex1 Construisez un type abstrait Ensemble implanté à partir d’une liste chaînée simple. Rappelonsqu’un ensemble est une liste non nécessairement ordonnée d’éléments de même type et qu’un mêmeélément ne peut se retrouver plusieurs fois dans l’ensemble.

Il faudra notamment pouvoir créer un ensemble, ajouter un élément à l’ensemble, vérifier si un élémentappartient à l’ensemble, obtenir le nombre d’éléments qui le composent.

Ex 2 Améliorez le programme de statistiques sur les lancers de dés en affichant un histogramme sousforme d’étoiles (une étoile par x unités), comme par exemple:

*************

Page 91: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 6 Les structures de contrôle - 85 -

Ex 3 Ecrivez une application qui prend en paramètres de la ligne de commande les coefficients a, b etc d’un trinôme du second degré et qui affiche un message décrivant le nombre et la valeurs des racineséventuelles.

Ex 4 Ecrivez une application qui simule le lancer d’un dé et qui affiche le nombre de lancers qu’il a fallu:• avant d’obtenir deux fois six consécutivement;• avant d’obtenir le résultat six après le résultat un.

Nous pourrions allonger indéfiniment la liste des exercices qui mettent en jeu les structures decontrôle disponibles en Java. Ces problèmes ne sont toutefois pas spécifiques à la POO. Tousles problèmes de cette nature traités habituellement avec les langages associés à laprogrammation impérative peuvent donc être repris ici, et en particulier, tous ceux qui ont pourobjectif de remplir des tableaux, par exemple. Nous n’allons donc pas allonger la liste desexercices possibles de manière inconsidérée. Nous vous renvoyons, pour cela, à des ouvragesplus anciens mais toujours bien d’actualité pour ce qui est de mettre en place les concepts liésà la programmation classique.

Page 92: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

61 Les classes d’héritage ne sont pas forcées d’implanter la méthode abstraite auquel cas ellesrestent abstraites. Ce sera alors à une des sous-classes de l’implanter.

Chapitre 7 D’autres mécanismes - 86 -

Chapitre 7 D’autres mécanismes

Aperçu

Outre les mécanismes déjà largement évoqués de l’encapsulation, de l’héritage, de la surcharge, dupolymorphisme, des liaisons dynamiques, il en existe d’autres qui contribuent, chacun à leur manière,à rendre les programmes plus efficaces, plus sûrs, mieux élaborés.

Les classes abstraites et les interfaces sont des notions de Java qui vont permettre, les unes, d’enrichirle mécanisme d’héritage, les autres de proposer une version un peu faussée mais pas trop complexede l’héritage multiple.

Nous dirons aussi un mot, dans ce chapitre, de la manière dont ils peuvent être détruits. Enfin, nousévoquerons et nous expliciterons le regroupement possible des classes et des interfaces en packages.

Les classes abstraites

Lorsque nous avons développé le mécanisme d’héritage, nous avons signalé qu’il était à la fois, unmoyen de spécialiser une classe existante, mais aussi un moyen d’en généraliser plusieurs d’entre elles,ayant des points communs non pris en compte au moment de leur création. En s’intéressant auprocessus de généralisation, on se rend compte que, plus on grimpe dans la hiérarchie, plus les classesdeviennent abstraites, dans le sens où il est possible de les caractériser sans trop spécifier la manièredont ces caractéristiques peuvent être établies.

Considérez les trinômes du second degré du genre ax² + bx + c dont on souhaite connaître les racines.Nous pouvons imaginer une classe Trinome très générale dont héritent les classesTrinomeSansRacine, TrinomeAvecRacineDouble, TrinomeAvecDeuxRacinesReelles, ou même uneclasse TrinomeAvecDeuxRacinesComplexes. La définition de la méthode calculerRacines seradifférente selon les classes qui héritent de la classe Trinome.

Pour les raisons que nous avons évoquées lorsqu’il fût question de polymorphisme, il est utile dementionner cette méthode au niveau de la classe Trinome, chaque classe d’héritage devant alors enremplacer la définition. Il est cependant difficile de la décrire à ce niveau sans faire perdre toute utilitéau graphe d’héritage. Ce type de difficulté est à la base de l’idée des méthodes et des classesabstraites. La méthode calculerRacines est déclarée au niveau de la classe Trinome sans toutefoisêtre définie. La méthode est abstraite. De ce fait, la classe Trinome le devient également et cela,même si d’autres méthodes de cette classe sont concrètes (une méthode getValeurDeA, par exemple).Pour devenir concrètes, les classes d’héritage doivent implanter la méthode calculerRacines61.

Page 93: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 87 -

Voici comment se traduisent, syntaxiquement, les propriétés d’abstraction de la méthode et, en conséquence, de la classe.

public abstract class Trinome{

protected double a, b, c;

...

public abstract void calculerRacines();

...

}

Observez l’absence d’accolades derrière la définition de la méthode abstraite.

L’intérêt des méthodes abstraites réside dans le fait qu’elles simplifient le travail du programmeur (qui,rappelons-le, ne s’occupe que des généralités) sans qu’il soit nécessaire de définir la procédurecorrespondante. Celle-ci sera obligatoirement et concrètement définie, soit dans ses sous-classesdirectes ou à un niveau de profondeur plus important dans la hiérarchie des classes. Une classe nedevient concrète que si toutes ses méthodes (y compris celles dont elle hérite) sont concrètes.

Signalons encore, même si c’est une évidence, qu’une classe abstraite ne peut être instanciée.

L’exemple suivant illustre l’emploi de la classe abstraite Trinome de même que le polymorphisme.Dans la description de la méthode main, le programmeur se contente de créer des trinômes dont il nesait, a priori, de quel type véritable ils seront.

public class AppAbstract{

public static void main(String [] args){

Trinome t1 = Trinome.creer(1.,2.,1.);Trinome t2 = Trinome.creer(1.,-1.,1.);Trinome t3 = Trinome.creer(1.,5.,6.);t1.calculerRacines();t2.calculerRacines();t3.calculerRacines();

}}

class Rho{

private double valeur;

public Rho(double a, double b, double c){valeur = b*b - 4*a*c;

}

public double getValeur(){return valeur;

}

Page 94: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 88 -

}

abstract class Trinome{

protected double a, b, c;Rho d;

public Trinome(double a, double b, double c, Rho d){this.a = a;this.b = b;this.c = c;this.d = d;

}

public static Trinome creer(double a, double b, double c){Rho d = new Rho(a,b,c);if (d.getValeur()<0){

return new TrinomeSansRacine(a,b,c,d);}else{

if (d.getValeur()==0){return new TrinomeAvecRacineDouble(a,b,c,d);

}else{

return new TrinomeAvecDeuxRacines(a,b,c,d);}

}}

public abstract void calculerRacines();}

class TrinomeSansRacine extends Trinome{

public TrinomeSansRacine(double a, double b, double c, Rho d){super(a,b,c,d);

}

public void calculerRacines(){System.out.println("Il n'y a pas de racines réelles.");

}}

class TrinomeAvecRacineDouble extends Trinome{

public TrinomeAvecRacineDouble(double a, double b, double c, Rho d){super(a,b,c,d);

}

public void calculerRacines(){System.out.println("La racine double est " + (-b/2*a));

}}

Page 95: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

62 Ce n’est pas un mauvais jeu de mots!

Chapitre 7 D’autres mécanismes - 89 -

class TrinomeAvecDeuxRacines extends Trinome{

public TrinomeAvecDeuxRacines(double a, double b, double c, Rho d){super(a,b,c,d);

}

public void calculerRacines(){System.out.println("Les racines sont " +((-b+Math.sqrt(d.getValeur()))/2*a) + " et " +((-b-Math.sqrt(d.getValeur()))/2*a) + ".");

}}

Devant la longueur du développement dans cette approche objet du problème, vous objecterez sansdoute62 qu’une approche classique est bien plus immédiate. Pour éliminer cette objection, il fauts’imaginer que les exigences peuvent encore et toujours évoluer. Que faire, par exemple, si on souhaiteenvisager, de manière optionnelle, la production de racines complexes? Comment associer simplementune interface graphique a une telle application? Examinons de suite le premier problème. Le secondsera résolu lors de la présentation et du développement des classes de l’interface graphique en Java.

Le graphe d’héritage que nous avons établi ne demande qu’à être complété. Nous créons ainsi uneclasse supplémentaire appelée TrinomeAvecRacinesComplexes, dans laquelle nous adaptons ladéfinition de la méthode calculerRacines. On pourrait s’étonner de ne pas voir cette nouvelle classeremplacer la classe TrinomeSansRacine. C’est que l’on veut se ménager la possibilité de faire le choixd’un calcul ou non des racines complexes. Il ne s’agit donc pas ici de créer une partition de l’ensembledes trinômes du second degré mais bien d’envisager une partition des possibilités de calcul que l’onveut se ménager.

Au niveau de la procédure (méthode) de création d’un trinôme, il faudra envisager cette possibilité etaussi prévoir le passage d’un booléen précisant si, oui ou non, d’éventuelles racines complexes doiventêtre calculées. La définition des classes de trinômes existantes n’est en rien modifiée par ceschangements.

Voici la nouvelle application. Les quelques changements sont indiqués en caractères gras.

public class AppAbstract2{

public static void main(String [] args){

Trinome t1 = Trinome.creer(1,2,1,true);Trinome t2 = Trinome.creer(1,-1,1,true);Trinome t3 = Trinome.creer(1,-1,1,false);Trinome t4 = Trinome.creer(1,5,6,true);

t1.calculerRacines();t2.calculerRacines();

Page 96: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 90 -

t3.calculerRacines();t4.calculerRacines();

}}

class Rho{

private double valeur;

public Rho(double a, double b, double c){valeur = b*b - 4*a*c;

}

public double getValeur(){return valeur;

}}

abstract class Trinome{

protected double a, b, c;Rho d;

public Trinome(double a, double b, double c, Rho d){this.a = a;this.b = b;this.c = c;this.d = d;

}

public static Trinome creer(double a, double b, double c, booleancomplexe){

Rho d = new Rho(a,b,c);if (d.getValeur()<0){

if (complexe){return new TrinomeAvecRacinesComplexes(a,b,c,d);

}else{

return new TrinomeSansRacine(a,b,c,d);}

}else{

if (d.getValeur()==0){return new TrinomeAvecRacineDouble(a,b,c,d);

}else{

return new TrinomeAvecDeuxRacines(a,b,c,d);}

}}

public abstract void calculerRacines();}

Page 97: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 91 -

class TrinomeSansRacine extends Trinome{

public TrinomeSansRacine(double a, double b, double c, Rho d){super(a,b,c,d);

}

public void calculerRacines(){System.out.println("Il n'y a pas de racines réelles.");

}}

class TrinomeAvecRacineDouble extends Trinome{

public TrinomeAvecRacineDouble(double a, double b, double c, Rho d){super(a,b,c,d);

}

public void calculerRacines(){System.out.println("La racine double est " + (-b/2*a));

}}

class TrinomeAvecDeuxRacines extends Trinome{

public TrinomeAvecDeuxRacines(double a, double b, double c, Rho d){super(a,b,c,d);

}

public void calculerRacines(){System.out.println("Les racines sont " +((-b+Math.sqrt(d.getValeur()))/2*a) +" et " + ((-b-Math.sqrt(d.getValeur()))/2*a) + ".");

}}

class TrinomeAvecRacinesComplexes extends Trinome{

public TrinomeAvecRacinesComplexes(double a, double b, double c, Rho d){super(a,b,c,d);

}

public void calculerRacines(){System.out.println("Les racines complexes sont " +((-b+Math.sqrt(-d.getValeur()))/2*a) + "i et " +((-b-Math.sqrt(-d.getValeur()))/2*a) + "i.");

}}

Il est évident que le polymorphisme peut s’étendre à d’autres méthodes qui seraient déclarées abstraitesdans la classe Trinome et définies dans chacune des classes d’héritage. A titre d’exercice, vouspouvez imaginer une méthode qui précise, par le renvoi d’une valeur booléenne, si une valeur donnéese trouve entre les racines du trinôme.

Page 98: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 92 -

Pour ce faire, il semble intéressant de définir une classe Racine dont les champs seraient garnis lors ducalcul de celles-ci. Si les champs de cette classe sont déclarés privés, des méthodes d’accès etd’altération sont nécessaires.

Les interfaces

Un des problèmes évoqués dans la section précédente consistait à lier l’application du calcul desracines à une interface graphique d’une certaine convivialité. Ce problème apparaît comme un casparticulier du sujet qui sera traité dans cette section, à savoir, les interfaces (tout court). Sa résolutionexigera également que nous nous intéressions aux classes graphiques prédéfinies de Java et que nousnous penchions sur le problème de la gestion des événements. Mais intéressons-nous d’abord auconcept d’interface dans sa généralité.

Nous venons de découvrir que la généralisation (à outrance) conduisait à ce que les classes situées dansles couches hautes de la hiérarchie soient des classes abstraites. Comprenez, par là, que la définitionde ces classes contient au moins une méthode abstraite et que ces classes ne peuvent avoir d’instances.

L’interface est un peu comme une classe abstraite mais dont la description ne comporte aucuneméthode concrète. L’interface définit, en quelque sorte, un contrat que certaines classes s’engagentà respecter sans préciser les termes de ce contrat. Il ne s’agit pas ici d’héritage au sens habituel. Onne dira d’ailleurs pas qu’une classe étend une interface, mais qu’elle l’implante.

interface Forme{

public abstract double surface();public abstract double volume()

}

Par ailleurs on trouvera...

class Point implements Forme{

private double x, y;

... // autres champs, constructeurs et méthodes

public double surface(){return 0.0;

}

public double volume(){return 0.0;

}}

Par ailleurs encore...

Page 99: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

63 On peut d’ailleurs se limiter à cela dans la description d’une interface.

Chapitre 7 D’autres mécanismes - 93 -

class Carre implements Forme{

private final String nom;private double cote;

public Carre(String n, double c){nom = n;cote = c;

}

public double surface(){returne cote*cote;

}

public double volume(){return 0.0;

}

public String getNom(){return nom;

}}

Commentaires

Par essence, les interfaces ont un rôle de porte d’entrée ce qui rend inutile le modificateur d’accèspublic.

Les interfaces ne comporteront pas de champs d’instances. En revanche, il est possible d’y définir desconstantes63 (champs déclarés public, static et final).

L’exemple choisi pourrait laisser croire qu’il est préférable d’utiliser une interface plutôt qu’une classeabstraite. Il n’en est rien. Si le programmeur est conscient de l’existence de points, de carrés, decercles,... dans son application, il est préférable qu’il crée une hiérarchie dont les classes supérieuresseront des classes abstraites. Les interfaces sont intéressantes parce qu’elles garantissent que lesclasses qui les implantent disposent des fonctionnalités qu’elles déclarent. A ce titre, le packagejava.lang comporte notamment deux interfaces qui portent les noms de Clonable et Comparable.On voit que les noms d’interface font plutôt référence à des propriétés que les classes d’implantationdevraient assurer à leurs instances. L’interface Clonable assure que les objets des classesd’implantation sont “reproductibles”, l’interface Comparable qu’il existe une technique pour lescomparer.

Héritage multiple

Pourquoi ce nouveau concept, alors qu’on pouvait se contenter des classes abstraites? Il faut sesouvenir qu’une classe ne peut hériter que d’une seule autre classe. Il arrive que dans certaines

Page 100: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

64 Imaginez que trois classes B, C et D héritent d’une même classe A et que, dans chacune de cesquatre classes, une méthode m() soit (re)définie. Imaginez qu’une classe E hérite à la fois de Cet de D (héritage multiple) sans redéfinir m(). Quelle méthode le compilateur doit-il choisirpour une invocation de la méthode m sur un objet instance de la classe E?

65 ...ce qui empêche l’invocation de méthodes qui ne sont pas connues, selon le cas, dePersonne ou de Sportif. Ainsi, l’invocation p2.getNom() provoquerait une erreur à lacompilation.

Chapitre 7 D’autres mécanismes - 94 -

situations, le programmeur souhaite disposer d’un mécanisme d’héritage multiple. L’héritage multiplen’est cependant pas simple à gérer64. C’est pourquoi Java propose ce concept d’interface qui secontente de déclarer les signatures de méthodes qui devront être définies au sein des classes quiprétendent l’implanter.

Dans l’exemple qui suit, l’application s’intéresse à des athlètes sélectionnés pour les JO. Ceux-ci sont,notamment et à la fois, des personnes dont on peut souhaiter obtenir le nom, et des sportifs dont onsouhaite connaître la discipline principale. La classe SelectionneJO va donc implanter les deuxinterfaces Personne et Sportif. C’est dire que les méthodes déclarées dans ces deux interfaces doiventy être définies si on veut que cette classe soit concrète.

Le polymorphisme y est également mis en évidence dans la mesure où les objets p1 à p4 qui sontréellement des objets de type SelectionneJO sont soit déclarés comme Personne, soit commeSportif 65. Bien entendu, vous pourriez rétorquer que cette façon de procéder en mélangeant lespersonnes et les sportifs n’est pas très intéressante ici. Il faut se dire que plusieurs applications sontsusceptibles d’employer ces interfaces et que, dans certains cas, le programmeur d’une application aurabesoin d’effectuer un traitement sur les personnes sans se préoccuper de savoir si ces personnes sontdes sportifs, des étudiants, des touristes ou qui que ce soit d’autre. Dans d’autres cas, il devra effectuerdes traitements concernant les sportifs, ce qui justifie des déclarations plus larges et moins spécifiques.C’est évidemment le prix à payer pour bénéficier du polymorphisme.

Le fichier contient en tout premier lieu, une instruction d’importation, celle du package java.util. Cetteimportation est rendue nécessaire par l’utilisation, plus en avant dans le programme, des classesprédéfinies que sont GregorianCalendar et la classe abstraite Calendar dont elle hérite.

import java.util.*;

public class AppInterface{

public static void main(String [] args){

// Testé le 11/04/2002 (à adapter)

Personne p1 = new SelectionneJO("Durant", "10/04/1983", "javelot");Personne p2 = new SelectionneJO("Dupont", "11/04/1983", "110m haies");Personne p3 = new SelectionneJO("Dupuis", "12/04/1983", "poids");Sportif p4 = new SelectionneJO("Dupont", "13/12/1983", "100m");

System.out.println("Le nom de la personne: " + p1.getNom());

Page 101: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 95 -

System.out.println("L'âge de la personne: " + p1.calculeAge() + "ans");System.out.println("Le nom de la personne: " + p2.getNom());System.out.println("L'âge de la personne: " + p2.calculeAge() + "ans");System.out.println("Le nom de la personne: " + p3.getNom());System.out.println("L'âge de la personne: " + p3.calculeAge() + "ans");System.out.println("La discipline du sportif: " +p4.getDisciplinePrincipale());

}}

interface Personne{

public abstract String getNom();public abstract long calculeAge();

}

interface Sportif{

public abstract String getDisciplinePrincipale();}

class SelectionneJO implements Personne, Sportif{

private String nom, discipline;private GregorianCalendar dateDeNaissance;

public SelectionneJO(String nom, String ddn, String discipline){

this.nom = nom;this.discipline = discipline;int annee = Integer.parseInt(ddn.substring(6,10));int mois = Integer.parseInt(ddn.substring(3,5)) - 1;int jour = Integer.parseInt(ddn.substring(0,2));dateDeNaissance = new GregorianCalendar(annee,mois,jour);

}

public String getNom(){

return nom;}

public String getDisciplinePrincipale(){

return discipline;}

public long calculeAge(){

GregorianCalendar now = new GregorianCalendar();int deltaM = now.get(Calendar.MONTH) -dateDeNaissance.get(Calendar.MONTH);

Page 102: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

66 Piège pour programmeur: les mois sont numérotés de 0 à 11 alors que les numéros des jourscommencent à 1.

Chapitre 7 D’autres mécanismes - 96 -

int deltaD = now.get(Calendar.DAY_OF_MONTH) -dateDeNaissance.get(Calendar.DAY_OF_MONTH);int deltaY = now.get(Calendar.YEAR) -dateDeNaissance.get(Calendar.YEAR);if (deltaM > 0 || (deltaM == 0 && deltaD >= 0)) return deltaY; elsereturn deltaY - 1;

}}

L’interface Personne annonce les deux méthodes getNom et calculeAge. L’interface Sportif annoncela méthode getDisciplinePrincipale. Cela signifie que, d’une manière ou d’une autre, la classeSelectionneJO devra les implanter toutes les trois. D’autre part, une interface ne pouvant posséderde champs d’instance, les champs nécessaires à l’implantation de ces méthodes devront se trouver dansla définition de la classe SelectionneJO. On y trouvera notamment les champs nom et discipline quisont de type String et le champ dateDeNaissance qui est de type GregorianCalendar. Le nomquelque peu étonnant de cette classe sera justifié sous peu.

Rappelons que les interfaces ont un accès public ce qui ne nécessite aucune précision explicite. Il enest de même pour leurs méthodes qui sont nécessairement abstraites. Les modificateurs d’accès publicet abstract ne sont donc pas nécessaires. Nous les avons mentionnés cette fois pour mettre enévidence les propriétés de ces méthodes. Nous ne le ferons plus dans la suite.

Il nous faut maintenant parler de la classe GregorianCalendar. En fait, ce dont nous avons besoin,c’est d’une classe permettant d’instancier des objets correspondant à des dates et comprenant uncertain nombre d’opérations liées à ce type d’information. Il existe deux classes prédéfinies portantle nom Date, l’une faisant partie du package java.util, l’autre faisant partie du package java.sql. Enréalité, ces classes représentent en interne les dates comme des nombres entiers exprimant le nombrede millisecondes s’étant écoulées depuis le 1er janvier 1970 à 0h. Une telle description de datecorrespond à l’utilisation d’un point de repère particulier dans un calendrier tout aussi particulier (enl’occurrence, le calendrier grégorien, celui qui est le plus utilisé dans notre civilisation). Parce que cettefaçon de voir est assez limitative, les concepteurs de Java ont imaginé une classe abstraite, Calendar,décrivant les propriétés générales des calendriers dont pourraient hériter diverses classes correspondantà différentes manières de se repérer dans le temps. En particulier, la classe GregorianCalendar estune classe concrète qui hérite de la classe Calendar. D’autres calendriers pourraient être implantéscomme des extensions de cette classe Calendar.

La classe GregorianCalendar possède plusieurs constructeurs intéressants, dont un qui prend troisentiers (type int) comme paramètres: le millésime, le numéro du mois66 et le numéro du jour dans lemois. Dans la définition du constructeur de la classe SelectionneJO, on l’utilise après avoir affecté leschamps nom et discipline et après que la chaîne de caractères correspondant à la date de naissanceait été soigneusement décomposée.

Page 103: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 97 -

La méthode calculeAge demande aussi un petit commentaire. Un autre constructeur est employé pourcréer la date du jour. Il reste à calculer la différence des années mais aussi celle des mois qui estabsolument nécessaire et celle des jours qui l’est parfois. Puis on renvoie la valeur en fonction durésultat des tests effectués. Vous observerez encore que la classe GregorianCalendar possède uneméthode get dont les paramètres peuvent être divers. La constante YEAR est définie au niveau de lasuperclasse Calendar, de même que les constantes MONTH et DAY_OF_MONTH. Elles sontévidemment static et final.

Enfin, la méthode main met tout cela en application. Les méthodes invoquées sont celles des classesde déclaration des objets.

Héritage multiple et interfaces prédéfinies

Dans l’illustration suivante, la classe MaClasse implante à la fois les interfaces Comparable etl’interface Clonable du package java.lang. Cela oblige MaClasse à décrire leurs méthodes. En fait,il n’y a que la seule méthode compareTo de l’interface Comparable à décrire car c’est la seuleméthode de cette interface et l’interface Clonable n’en comporte pas. Elle garantit simplement auxclasses qui l’implantent que la méthode clone de la classe Object fonctionnera sans générerd’exception.

class MaClasse implements Comparable, Clonable{public int compareTo(Object o){

...}

...}

Vous pouvez le constater ici, les interfaces sont également utilisées pour caractériser des propriétés quedevraient posséder les objets des classes qui les implantent.

Les interfaces graphiques et la gestion des événements

Un autre secteur dans lequel les interfaces s’avèrent utiles est celui des applications fonctionnant parl’intermédiaire d’interfaces graphiques. En fait, le mot interface utilisé jusqu’ici n’a pas un senscomplètement différent de celui qu’il recouvre dans l’expression interface graphique. L’interfacegraphique est aussi la couche supérieure par le biais de laquelle l’utilisateur de l’application vas’adresser au programme sans se préoccuper de ce qui se passe en arrière-plan.

Bien entendu, il est difficile de mettre au point une application interactive sans gérer des événements telsque des clics de souris sur des boutons, sur des items de listes ou encore la pression de la touche devalidation après avoir rempli un champ de données telle une zone de texte, par exemple. Ceci nousamènera à nous intéresser au modèle des événements tel qu’il est conçu en Java. Nous en parleronsplus en détail dans le chapitre qui suit. Toutefois, afin d’illustrer le concept d’interface en liaison avecla gestion des événements, analysons une application pas très compliquée, le déclenchement et l’arrêt

Page 104: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 98 -

d’un chronomètre. Nous n’intégrerons pas dans cette application élémentaire d’éléments graphiquespour l’instant.

Quelques outils

L’interface ActionListener est une interface prédéfinie. Les instances des classes qui l’implantentseront des “écouteurs d’action”. Dès lors, ces classes doivent posséder une définition de la méthodeActionPerformed qui décrit les traitements à effectuer lorsqu’un message de réalisation d’événementleur parvient. L’association entre un objet émetteur et un objet récepteur (l’écouteur) peut se faire dediverses manières.

Dans le cas présent, la classe AfficheurDeTemps implante l’interface ActionListener. Chaque objetde cette classe possède un champ appelé topChrono, invariable (donc initialisé à la création de l’objet)qui contient (sous forme d’une valeur numérique liée au 1er Janvier 1970) le temps initial. La méthodeactionPerformed construit un nouvel “instant” à chaque réception d’un message (en principe toutesles 5 secondes) et affiche la différence entre le moment présent et le moment de la création du chrono.

La méthode main de l’application construit un chrono (objet de type déclaré ActionListener etréellement, objet de type AfficheurDeTemps) puis crée un objet de la classe prédéfinie Timer dontles paramètres de construction sont un délai (5000 millisecondes) et l’écouteur à avertir (le chrono).Le Timer est démarré grâce à la méthode start et une boîte de dialogue élémentaire permet de lestopper implicitement.

Cette application est rudimentaire au niveau de l’affichage. Elle sera améliorée lors de notre découvertedes classes graphiques (bouton de démarrage, d’arrêt, de réinitialisation du chrono et affichage dutemps dans une zone de texte, par exemple).

Le début du fichier contient des instructions d’importation nécessaires vu le nombre de classesprédéfinies employées. La justification de leur présence est donnée en commentaires.

import java.util.*; // pour Dateimport java.awt.event.*; // pour ActionEvent et ActionListenerimport javax.swing.*; // pour JOptionPaneimport javax.swing.Timer; // pour éviter le conflit avec la classe

// Timer de java.util

public class AppChrono{

public static void main(String [] args){

ActionListener chrono = new AfficheurDeTemps();Timer t = new Timer(5000,chrono);t.start();JOptionPane.showMessageDialog(null, "Fin du programme?");System.exit(0);

}}

Page 105: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

67 Le Francophone, plus délicat, parle de ramasse-miettes!

68 “Automatique” ne signifie pas “systématique”. Ainsi, le Garbage Collector n’agit pastoujours sauf en cas d’absolue nécessité car il exécute un travail de très faible priorité parrapport aux autres tâches.

Chapitre 7 D’autres mécanismes - 99 -

class AfficheurDeTemps implements ActionListener{

private final Date topChrono = new Date();

public void actionPerformed(ActionEvent e){

Date now = new Date();System.out.println("Temps écoulé: " + (now.getTime() -topChrono.getTime())/1000 + " secondes");

}}

Le garbage collector

Il est temps de dire un mot de la manière dont les objets disparaissent. Si la naissance d’un objet detype A correspond à une instruction équivalente à

A a = new A();

sa destruction peut se faire en douceur ou de manière un peu plus brutale. En Java, il existe unmécanisme appelé Garbage Collector (littéralement “collecteur d’ordures” ou “éboueur67”) quis echarge de rendre disponible la mémoire occupée par un objet dès que celui-ci n’est plus référencé.Cela ne signifie pas que l’objet disparaît, mais qu’il est susceptible de disparaître si des ressources enmémoire sont nécessaires. Le programmeur ne doit donc pas se préoccuper de détruire les objets.Ceux-ci sont détruits de manière automatique68. Il y a donc des constructeurs mais pas de destructeurs.

Il peut cependant arriver que le programmeur souhaite cette disparition immédiate. C’est le cas,notamment, si d’autres ressources que la mémoire sont monopolisées par un objet, comme un fichier,par exemple. Dans ce cas, il peut forcer la collecte en utilisant la méthode finalize que possèdenttoutes les classes puisqu’elle est héritée de la classe Object. Dans cette méthode, il pourra s’arrangerpour décrire les traitements permettant de libérer les ressources libérables comme la fermeture d’unfichier, par exemple.

Les packages

La prolifération des classes conduit rapidement à envisager des possibilités de regroupement de celles-ci, mais aussi leur archivage, dès que le programmeur considère qu’elles constituent des modulesrelativement bien élaborés. La solution offerte en Java pour résoudre ces difficultés s’appellepackages.

Page 106: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

69 Une classe Tableau pourrait être définie dans un package avec des fonctionnalités propre àune ou des applications particulières sans empêcher qu’une autre classe Tableau, avecd’autres fonctionnalités, soit définie dans un autre package.

Chapitre 7 D’autres mécanismes - 100 -

Les packages sont des unités de regroupement logique de classes, d’interfaces et éventuellementd’autres packages. Outre leur fonction de regroupement, les packages fonctionnent un peu comme desbarrières, permettant au programmeur d’utiliser des noms plutôt génériques dans un contexte assezparticulier en évitant la confusion avec les mêmes noms utilisés dans d’autres contextes69. De même,ils jouent un rôle dans l’accessibilité et la disponibilité de certains membres de classes.

Déclaration

La déclaration d’appartenance à un package se fait au niveau du fichier source, au tout début de celui-ci. Une telle déclaration doit être unique et engage donc toutes les classes et toutes les interfaces dontles définitions figurent dans le fichier source. Pour le compilateur, tous les noms des types déclarés dansun package sont préfixés du nom de ce package. Ce préfixe est ajouté automatiquement à lacompilation.

Voici un extrait d’un fichier source:

package geometrie;

class Point{

...

}

class Droite{

...

}

Dans cet exemple, les noms complets des classes sont en réalité geometrie.Point et geometrie.Droite.

Lorsque le programmeur fait référence à une classe du package, il peut utiliser le nom court de la classe. A l’intérieur du package, la référence complète est inutile. En revanche, si la classe fait partie d’unautre package, il peut utiliser le nom complet.

A supposer que la classe A ne fasse pas partie du package geometrie:

class A{

...

geometrie.Point p = new geometrie.Point();

...

Page 107: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

70 C’est particulièrement vrai en ce qui concerne les classes prédéfinies de Java comme, parexemple, les classes Date de java.util et de java.sql, Timer de java.util et javax.swing etc.

71 En interne, il est possible de mettre aux points diverses stratégies de nommage quigarantissent cette unicité.

Chapitre 7 D’autres mécanismes - 101 -

}

Il existe cependant une autre possibilité, celle d’importer tout ou partie de certains packages. Lesclasses de ces packages sont alors de nouveau accessibles par leur nom court.

Toujours en supposant que la classe A ne fasse pas partie du package geometrie:

import geometrie.*;

public class A{

...

Point p = new Point();

...

}

ou

import geometrie.Point;

...

Dans ce second cas, seule la classe Point sera accessible dans le package geometrie.

Il est donc possible que des classes différentes portent le même nom court70.

D’une manière générale, il est possible d’utiliser les noms longs des classes ou leur nom court, si leurspackages sont importés. Il est également possible de panacher les deux techniques.

Les noms de packages

L’idée de la réutilisabilité peut se limiter aux classes écrites par le programmeur lui-même, mais aussis’étendre aux classes écrites par les programmeurs d’une organisation et, pourquoi pas, par lesprogrammeurs du monde entier (la belle idée!). A cette fin, le choix des noms de packages doit êtretel que l’unicité soit respectée dans toute l’étendue du domaine d’utilisation des classes qu’ilscontiennent71.

A l’échelle d’Internet, les choses ne sont pas simples à mettre en place. La recommandation qui estfaite est d’utiliser le nom de domaine de l’organisation (dans l’ordre inverse) et une suite dont l’unicitépeut être plus aisément gérée.

Page 108: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

72 Le choix des majuscules pour le premier terme est une précaution supplémentaire pour évitertoute collision avec les noms de packages de ceux qui ne respectent pas la convention.

73 Si on ne précise pas à quel package appartiennent les éléments d’un fichier source, ilsappartiennent au package par défaut qui est un package sans nom.

74 Pour rappel, dans un fichier contenant plusieurs définitions de classes, une seule peut êtredéclarée d’accès public. Les autres ne sont pas réutilisables! Souvent, il est nécessaire derépartir ces définitions dans plusieurs fichiers pour permettre un accès à ces différentesclasses.

Chapitre 7 D’autres mécanismes - 102 -

Le préfixe de tous les noms de packages regroupant les programmes développés par Etienne Vandeput travaillant aux FUNDPdevraient commencer par BE.ac.fundp.eva (pour autant que les identifiants en trois lettres, première lettre du prénom et deuxpremières lettres du nom, soient gérés sans ambiguïté au niveau de l’organisation)

Si chacun s’engageait à respecter cette règle, il existerait donc dans le monde un seul package dont le nom estBE.ac.fundp.eva.geometrie72.

Il reste que les environnements de développement exigent que les fichiers contenant le code des classesd’un package se trouvent dans des répertoires particuliers et qu’il y ait des correspondances entre lesnoms des répertoires et ceux des packages. Nous allons examiner ces exigences par la suite.

Accès

Si les interfaces sont par essence d’accès public, il n’en est pas de même des classes. En l’absencede modificateur d’accès, elles sont accessibles de partout dans le package73. De la sorte, l’accès pardéfaut est package.

Rappelons qu’une classe a accès à toutes les classes de son package et aux classes publiques74 desautres packages. L’emploi des noms courts pour ces dernières n’a de sens que si les packages fontl’objet d’une instruction d’importation. Dans le cas contraire, le nom long doit être employé.

Hiérarchie

Les packages peuvent être emboîtés à la manière des répertoires dans un système de fichiers. Cettepossibilité permet aux programmeurs de créer une hiérarchie logique permettant aux utilisateurspotentiels de s’y retrouver sans trop de difficultés. Il n’y a pas d’accès privilégiés entre les membresde packages emboîtés.

Les classes prédéfinies de Java se trouvent toutes dans la hiérarchie des packages java et javax. A titre indicatif, le package java necontient pas de classes ni d’interfaces. Il ne contient que des packages comme, par exemple, le package java.lang.Le package java.lang contient des classes dont la classe Math, la classe String et bien d’autres mais contient aussi le packagejava.lang.ref qui, lui, ne contient que des classes.

Il n’y a pas d’accès privilégiés entre les classes de java.lang.ref et celles de java.lang qui sont des packages tout à fait différents.

Page 109: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

75 Dans l’environnement Windows, les classes prédéfinies de Java se retrouvent dans les fichiersi18n.jar et rt.jar du sous-répertoire \jre\lib créé dans le dossier d’installation de Java.

Chapitre 7 D’autres mécanismes - 103 -

Le programmeur est amené à créer ses propres packages, pour pouvoir réutiliser ses propres classesou les faire partager à d’autres et pour créer une séparation logique entre les classes qu’il adéveloppées et celles qu’il utilise (classes “système” et/ou classes provenant de diverses librairies).

Stockage des classes

Les classes sont stockées dans des fichiers .class mais peuvent aussi se cacher derrière des fichierscompressés (.jar pour Java archives)75. Le programmeur peut aussi créer ses propres fichiers .jaren se servant de l’utilitaire jar livré avec le kit de développement.

La recherche des classes

La notion de package trouve tout son sens dans un contexte de réutilisation des classes. Un fichiercontient au plus une instruction package, fixant le package d’appartenance des classes et interfacesdu fichier et éventuellement, une ou plusieurs instructions d’importation. Pour qu’une classe soitréutilisable, il faut qu’elle ait été déclarée public et enregistrée dans un package. La réutilisation se faitau moyen d’une instruction import.

Les classes sont recherchées par l’interpréteur à divers endroits:• dans le répertoire courant,• dans certains répertoires d’installation de Java où les classes prédéfinies figurent à l’état compressé

dans des fichiers particuliers,• en suivant le classpath et les informations correspondant aux packages des classes.

Si des classes du packages par défaut (associé au classpath) font appel à des classes d’un package,ces dernières doivent se trouver dans des sous-répertoires correspondant à la hiérarchie définie par lepackage.

Si le classpath est c:\jdk\mesclasses et qu’une classe AppDroite du package par défaut utilise une classe Point et une classe Droite,toutes deux importées du package eva.geometrie, les fichiers Point.class et Droite.class doivent se trouver enc:\jdk\mesclasses\eva\geometrie sans quoi l’interpréteur ne pourra accomplir son travail.

Notez encore que le compilateur ne vérifie pas que les fichiers sources (.java) se trouvent dans les bonssous-répertoires. Il y a donc intérêt à leur faire respecter la hiérarchie des fichiers .class.

Exercices

Ex 1 Faites imprimer le calendrier du mois en cours en marquant d’un astérisque le chiffre du jour.Utilisez un modèle semblable au suivant:

Mai 2002

Page 110: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 104 -

Lu Ma Me Je Ve Sa Di 1 2 3 4 5 6 7 8* 9 10 ...

Ex 2 Faites afficher l’heure toutes les minutes dans une fenêtre de l’OS.

Ex 3 Ecrivez une application utilisant les classes abstraites pour implanter le type abstrait file, de sorteque ce type soit utilisable avec n’importe quel type de données (files d’entiers, files de chaînes decaractères, files de booléens et même file de files de...).

Solution

Il existe plusieurs solutions à ce type de problème. Une solution consiste à créer une classe FileDe,puis de l’étendre par différentes classes telles FileDEntiers, FileDeChaines,... Une autre solution,celle qui est choisie ici, consiste à définir le type des informations stockées dans la file comme unparamètre.

On définit les opérations habituelles sur une file. On fait un choix pour l’implantation de la file, parexemple, une liste simplement chaînée. On considère que le type T est un paramètre de la file et ondéfinit une classe T qui va pouvoir être étendue de diverses manières.

Les opérations sur une file:

• la créer;• vérifier si elle est vide ou non;• y ajouter un élément;• en retirer un élément (le premier de la file) et en avoir connaissance;

plus d’autres opérations moins fondamentales mais toutefois utiles telles:

• afficher les éléments de la file;• vérifier si deux files sont égales ou non;• ...

La file est implantée de la manière suivante: un pointeur vers le premier et un pointeur vers le dernierélément de la file, ce qui simplifie à la fois les procédures d’affichage, de retrait et d’ajout d’éléments.

La procédure de création est déclarée static et le code des méthodes classiques de traitement de lafile ne posent pas de gros problèmes.

public class File {

Element premier;Element dernier;

public static File creer(){return new File();

}

public boolean estVide(){return premier==null;

Page 111: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 105 -

}

public void ajouter(Element e){if (estVide()){

premier = e;}else{

dernier.suivant = e;

}dernier = e;

}

public T retirer(){T t = premier.info;premier = premier.suivant;return t;

}...

Observez qu’on retire un élément de type T (a priori inconnu). On suppose que cette méthode n’estd’application que lorsque l’on est sûr que la file n’est pas vide! La méthode d’ajout est légèrementdifférente selon que la file est vide ou non (affectation de premier si la file est vide).

La méthode d’affichage demande un petit commentaire. L’affichage de la file est reporté sur l’affichagede chacun de ses éléments, à partir du premier.

La méthode egal, qui permet de comparer deux files, démarre à partir des premiers éléments (s’ilsexistent) de chacune des files. La boucle n’est pas parcourue si la comparaison de deux éléments afourni un résultat négatif (r a la valeur false) ou si l’un des deux éléments n’existe pas. Dans les autrescas, on avance dans les deux files et on compare. La valeur retournée est celle de r corrigée enobservant que la valeur true n’a de sens que si les deux pointeurs s’évaporent en même temps.

...public boolean egal(File f) {

Element e1, e2;e1 = this.premier;e2 = f.premier;boolean r = true;

while (r && (e1!=null && e2!=null)){r = (e1.info.egal(e2.info));e1 = e1.suivant;e2 = e2.suivant;

}

return r && (e1==null && e2==null);}

Page 112: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 106 -

public void afficher(){

Element e = premier;

while (e != null){e.affiche();e = e.suivant;

}}

}

Les files sont définies à partir de leur premier et de leur dernier élément. Il convient donc de définir uneclasse Element qui donnera à la file les caractéristiques d’une liste chaînée (un Element possède uneréférence vers un suivant. L’information contenu dans un Element est de type T (inconnu).

public class Element {

T info;Element suivant;

public Element(T t) {info = t;

}

public void affiche(){info.affiche();

}}

On construit un Element en lui fournissant une info de type T. La responsabilité de l’affichage estreportée sur la classe T.

Le type inconnu est représenté par une classe T qui est une classe abstraite définissant deux méthodes,une méthode egal pour la comparaison de deux éléments de type T et une méthode affiche, toutesdeux indéfinissables à ce niveau. Ces méthodes sont donc abstraites.

public abstract class T {

public abstract boolean egal(T t);public abstract void affiche();

}

Il reste à exploiter la définition de cette classe abstraite en l’étendant par des classes concrètescorrespondant à des informations de divers types: entiers, chaînes de caractères,... Les définitions declasses qui suivent illustrent cette possibilité.

Page 113: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 107 -

public class Entier extends T {

int valeur;

public Entier(int i){valeur = i;

}

public boolean egal(T e){Entier en = (Entier)e;return (valeur == en.valeur);

}

public void affiche(){System.out.print(valeur + " ");

}}

Une instruction mérite un commentaire, c’est l’instruction:

Entier en = (Entier)e;

Il s’agit d’un transtypage. En effet, le compilateur oblige la méthode egal à prendre un paramètre detype T. De ce fait, l’information valeur n’est pas directement accessible via la variable e. Letranstypage permet d’affecter à une nouvelle variable, la référence vers l’objet réel, ce qui permet deretrouver l’information recherchée.

On retrouve des définitions assez semblables dans la classe ChaineDeCaracteres.

public class ChaineDeCaracteres extends T {

String valeur;

public ChaineDeCaracteres(String s){valeur = s;

}

public boolean egal(T s){ChaineDeCaracteres st = (ChaineDeCaracteres)s;return (valeur.equals(st.valeur));

}

public void affiche(){System.out.print(valeur + " ");

}}

On utilise également le transtypage et la méthode equals, particulière à la classe String, effectue lacomparaison.

Page 114: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 108 -

Enfin, il reste à écrire une classe application pour tester tout cela. Deux files d’entiers sont constituées,par ajout et retraits d’éléments. Des affichages et des comparaisons sont effectués. Une file de chaînesde caractères est également créée et manipulée.

import eva.abstracttypes.*;

public class AppAbstract5 {

public static void main(String [] args){

// Files de nombres entiers

// Création d'une première file

File f = File.creer();f.ajouter(new Element(new Entier(4)));f.ajouter(new Element(new Entier(-2)));f.ajouter(new Element(new Entier(9)));

// Affichage

System.out.print("Première file: ");f.afficher();System.out.println();

// Retrait d'éléments

System.out.print("Je retire ");f.retirer().affiche();f.retirer().affiche();System.out.println();

// Nouvel affichage

System.out.print("Il reste ");f.afficher();System.out.println();

// Création d'une deuxième file

File f2 = File.creer();f2.ajouter(new Element(new Entier(9)));

// Affichage

System.out.print("Deuxième file: ");f2.afficher();System.out.println();

// Comparaison des files

if (f.egal(f2)) System.out.println("Les deux files sont égales.");else System.out.println("Les deux files sont différentes.");

Page 115: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 7 D’autres mécanismes - 109 -

// Nouveau retrait

System.out.print("Je retire ");f.retirer().affiche();System.out.println();

// Nouvelle comparaison

if (f.egal(f2)) System.out.println("Les deux files sont égales.");else System.out.println("Les deux files sont différentes.");

// File de chaînes de caractères

// Création d'une file

File f3 = File.creer();f3.ajouter(new Element(new ChaineDeCaracteres("Programmation OO")));f3.ajouter(new Element(new ChaineDeCaracteres("Java")));f3.ajouter(new Element(new ChaineDeCaracteres("Machine virtuelle")));

// Affichage

System.out.print("Troisième file: ");f3.afficher();System.out.println();

// Retrait d'un élément

System.out.print("Je retire ");f3.retirer().affiche();System.out.println();

// Nouvel affichage

System.out.print("Il reste ");f3.afficher();System.out.println();

}}

Toutes sortes de classes représentant divers types d’informations peuvent désormais étendre la classeT.

Page 116: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

76 Visual Basic, par exemple

77 ...aussi appelés contrôles graphiques.

Chapitre 8 GUI et gestion des événements - 110 -

Chapitre 8 GUI et gestion des événements

Le modèle des événements

Lorsqu’une application se déroule en arrière-plan d’un environnement graphique, et c’est souvent lecas des logiciels qui doivent interagir avec leur utilisateur, l’environnement d’exécution doit gérer unesérie d’événements susceptibles de se produire (c-à-d., vérifier constamment s’ils se produisent ounon). Y a-t-il eu un clic de souris sur tel bouton, l’utilisateur a-t-il modifié le contenu de telle zone detexte, a-t-il choisi un item dans une liste, un délai s’est-il écoulé, etc. Avec certains langages76, lesobjets graphiques77 et les événements qui leur sont associés sont prédéfinis. Il reste au programmeurà décrire le code correspondant à tel et tel événement associé à tel ou tel contrôle graphique. En Java,le modèle des événements est un peu plus sophistiqué, permettant davantage de souplesse.

Si les différentes sources d’événements (boutons, barres de défilement, zones de texte,...) sont fixés,Java laisse au programmeur la possibilité de désigner les objets qui sont à l’écoute de ces événementset qui peuvent décider des traitements à effectuer.

Ce regain de souplesse s’obtient au détriment d’une légère complexification du code. Mais sonanalyse, un peu plus délicate, est une question d’habitude.

Il faut donc distinguer plusieurs acteurs (classes d’objets) dans la gestion des événements.

• Les sources d’événements sont des objets dont les classes possèdent des méthodes permettantleur association à un ou plusieurs écouteurs d’objets. Lorsqu’un événement leur “arrive”, ils notifienttous les écouteurs qui leur sont associés en leur envoyant un objet événement qui contient diversesinformations concernant l’événement qui s’est produit.

• Les événements sont des objets créés à l’initiative des sources d’événements et qui sont destinésaux écouteurs d’événements.

• Les écouteurs d’événements qui peuvent être des objets de n'importe quelles classes pourvuqu'elles implantent une ou plusieurs interfaces spécifiques:

< ActionListener

< MouseListener

< FocusListener

< …

ou qu'elles étendent une classe particulière:

Page 117: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 111 -

< WindowAdapter

< MouseAdapter

< ...

Exemple de création d’une association source-écouteur:

ActionListener objetEcouteur = new ...; // Objet quelconque dont la classe// implante ActionListener

Button b = new Jbutton(“Démarrer”); // Crée un bouton dont le libellé// est “Démarrer”

b.addActionListener(objetEcouteur);

NB Ces instructions ne suffisent pas. Il en faut d’autres, par exemple pour que le bouton s’affiche.

Comme nous l’avons déjà vu dans un exemple du chapitre précédent, une classe qui implante l’interfaceActionListener doit décrire une méthode appelée actionPerformed et qui prend en paramètre unobjet de type ActionEvent.

public void actionPerformed(ActionEvent e){

...

}

La gestion des événements est donc réalisée de la manière suivante: lorsque l’objet source constate quel’événement se produit, il invoque la méthode appropriée de tous les écouteurs de ce type d’événementen leur passant comme paramètre un objet événement dont les informations pourront servir aux objetsécouteurs.

Si l’objet écouteur est un MouseListener, il devra savoir que faire lors de l’invocation des cinq méthodes mouseClicked,mouseEntered, mouseExited, mousePressed et mouseReleased. Bien entendu, ces cinq méthodes devront être définies dans laclasse de l’objet écouteur, quitte à ce que cette définition soit vide pour la plupart. Par exemple, on peut seulement s’intéresser àl’endroit où le clic s’est produit. Dans ce cas, seule la définition de la méthode mouseClicked contiendra des instructions.

Comme il y a souvent plusieurs méthodes dans la définition d’une interface, il est parfois préférabled’étendre une classe, plutôt que d’implanter une interface. Bien sûr, une classe ne peut étendre qu’uneseule autre classe, mais il n’est pas nécessaire de redéfinir les méthodes qui ne sont pas exploitées sicelles-ci sont définies à plus haut niveau.

Page 118: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

78 Un exemple: préférez la classe JTextField du package javax.swing à la classe TextField dupackage java.awt.

Chapitre 8 GUI et gestion des événements - 112 -

Par exemple, la gestion de la fermeture d’une fenêtre peut se faire par un objet d’une classe étendant la classe WindowAdapter Ilexiste aussi une classe MouseAdapter.

Il n’est évidemment pas possible de décrire toutes les interfaces et toutes les classes prédéfinies qui sontau service des objets potentiellement écouteurs d’événements. Nous vous renvoyons pour cela à ladocumentation.

Les objets événements générés dépendent du type d’événement qui s’est produit et ne sont envoyésaux objets écouteurs que dans la mesure où ces objets sont à l’écoute de ce type d’événement.

Si la classe d’un objet implante l’interface FocusListener, elle devra définir deux méthodes: focusGained et focusLost. L’objet ferapartie de la liste des écouteurs d’une ou de plusieurs sources. Lorsque l’une de ces sources perdra le focus, elle invoquera laméthode focusLost de l’objet avec comme paramètre un FocusEvent.

AWT et Swing

Le modèle des événements est développé au travers des classes et des interfaces du packagejava.awt. AWT signifie Abstract Windowing Toolkit. C’est une boîte à outil pour réaliser dufenêtrage de manière abstraite. Le package contient également des définitions pour les composants tels,les boutons de toutes sortes, les listes déroulantes, les zones de texte,... Toutefois, ces composantssont considérés comme lourds dans la mesure où ils ont été écrits en tenant compte des spécificités desOS. De nouveaux composants ont été réécrits en Java et sont moins dépendants de tel ou telenvironnement. Ils font partie du package javax.swing.

Pour ce qui est des composants, il est déconseillé d’utiliser les composants d’AWT78 qui seront, àterme, fortement dépréciés.

Que de hiérarchies!

Ces considérations nous amènent à examiner, une fois de plus, les nombreuses classes prédéfinies deJava. C’est une démarche inévitable dans un contexte de développement. Nous citerons simplementici la hiérarchie des objets graphiques, que l’on retrouve en double, compte tenu de la remarque quiprécède.

Il existe une classe Button dans le package java.awt et une classe Jbutton dans le package javax.swing. Elles n’ont rien à voir l’uneavec l’autre, même si leur but est d’offir les fonctionnalités attendues d’un bouton. Leurs branches hiérarchiques se rejoignent auniveau de la classe Component qui est une sous-classe directe de la classe Object. Il en est de même pour la plupart des autres

Page 119: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 113 -

composants graphiques avec toutefois une exception notable pour les classes Frame et JFrame, JFrame héritant directement deFrame.

Nous pouvons évoquer également la hiérarchie des événements qui héritent tous de la classeAWTEvent, mais également la hiérarchie des exceptions dont nous parlerons plus loin. Il convient devous documenter un peu à ce sujet, dans la mesure où presque toutes ces classes héritent d’un nombreconsidérable de méthodes.

Interfaces et programmation événementielle

Afin d’illustrer convenablement le concept d’interface développé au chapitre précédent et lefonctionnement du modèle des événements, intéressons-nous à deux applications.

La première, montre que le concept d’interface est utile dans le cadre de la programmationévénementielle. Plusieurs interfaces sont en effet prédéfinies et leur utilisation facilite ce type deprogrammation.

La seconde met en évidence le fait que le programmeur est parfois amené à construire lui-même sespropres interfaces pour conférer à ses modules (objets, programmes, logiciels) des qualités deréutilisabilité. C’est particulièrement le cas lors de la conception de logiciels ayant une composanteimportante d’interaction avec leurs utilisateurs. On imagine qu’il est possible de développer des écransde saisie, d’affichage, agrémentés de contrôles graphiques, soignant particulièrement certains aspectsimportants de la conception des IHMs (interfaces homme-machine). Par ailleurs, il est raisonnable depenser que le développement de l’application proprement dite, n’aura que peu à voir avec ledéveloppement de l’environnement de travail de l’utilisateur. Il serait également stupide de considérerque ces deux catégories d’objets (écrans et objets de l’application) n’ont rien en commun. Nousverrons que le concept d’interface, tel qu’il est prévu en Java, permet de dresser des sortes de contratsentre les développeurs des deux catégories, autorisant par là un travail assez indépendant des uns etdes autres.

Chacune de ces deux applications sera traitée en plusieurs phases, permettant l’introduction de l’un oul’autre concept supplémentaire, suite à la rencontre de certaines difficultés.

Le détecteur de multiples de 7

Dans un champ texte d’une boîte de dialogue classique, on veut pouvoir introduire des nombres dontun programme vérifie qu’ils sont multiples de 7 ou non.

Première version

Elle fait afficher une boîte de dialogue contenant un champ texte permettant d’introduire un nombreentier et un bouton demandant à l’application de vérifier si ce nombre est un multiple de 7. Le résultat

Page 120: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

79 Une étiquette ou label est un composant d’une boîte de dialogue que l’utilisateur ne peutmodifier de manière dynamique. Cela ne signifie pas que le programme ne puisse pas le faire. C’est d’ailleurs ce qu’on lui demande ici. Normalement, les étiquettes sont utilisées pourétiqueter les champs de données.

80 Pour rappel, cette classe est une exception à la réécriture complète des composants graphiquespuisqu’elle hérite directement de la classe Frame du package java.awt.

81 Cette façon de faire est souvent économique (en lignes de code) dans le cas où le nombred’événements à contrôler n’est pas très élevé.

Chapitre 8 GUI et gestion des événements - 114 -

est affiché sous forme d’une étiquette79. Cette version ne contrôle pas certains événements tels que laterminaison correcte du programme à la fermeture de la boîte de dialogue ou encore, l’introduction devaleurs erronées (des caractères qui ne soient pas des chiffres) ou de valeurs trop élevées.

Intéressons-nous d’abord à la création de la boîte de dialogue. Celle-ci sera l’instance d’une classequi héritera d’une classe prédéfinie, afin de profiter de tout une série de fonctionnalités (bouton defermeture, de maximisation ou de réduction en icône, barre de titre, etc.). Cette classe est la classeJFrame80.

L’événement essentiel est, évidemment, le clic sur le bouton de vérification. Un objet d’une classeimplantant une interface d’écoute doit être à l’écoute de ce bouton qui détectera l’événement et luienverra un message, le cas échéant. Il est toujours possible, et c’est parfois souhaitable, de créer detoute pièce une classe qui donnera naissance à ce type d’objet écouteur. Dans notre cas, puisqu’uneclasse est nécessaire à la construction de la boîte de dialogue, il nous est possible de faire d’une pierredeux coups en demandant à la boîte de dialogue elle-même d’être à l’écoute du bouton81.

La classe à construire s’appelle BoiteMultiple et doit donc, à la fois, implanter l’interfaceActionListener et étendre la classe JFrame. Les groupes d’instructions nouvelles sont partiellementcommentés dans le code qui suit. La documentation sur les classes et méthodes utilisées est disponibleen ligne.

class BoiteMultiple extends JFrame implements ActionListener {

JButton b;JTextField jt;JLabel lm;

// Construction de la boîte de dialoguepublic BoiteMultiple(){

// Paramétrage de la fenêtresetTitle("Multiple de 7?");setSize(450,80);setLocation(300,300);

// Obtention d'une référence vers le conteneurContainer c = getContentPane();

Page 121: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

82 Il existe aussi une classe GridLayout qui permet notamment de définir des lignes et descolonnes et une classe BorderLayout dont la mise en page un peu plus sophistiquée définitcinq régions: une région centrale et quatre autres correspondant aux points cardinaux.

83 C’est bien un gérant car sa classe implante l’interface LayoutManager.

84 Pour les curieux, un objet de type JFrame contient un champ rootPane de type JRootPane etles objets de type JRootPane ont un champ contentPane de type Container. C’est cetteréférence qui est renvoyée par la méthode getContentPane. Et vive la hiérarchie et l’héritage!

Chapitre 8 GUI et gestion des événements - 115 -

// Création d'un gestionnaire de mise en page pour le conteneurFlowLayout fl = new FlowLayout();c.setLayout(fl);

// Création des composants de la fenêtreJLabel ln = new JLabel("Nombre à examiner:");jt = new JTextField(8);b = new JButton("Vérifier");lm = new JLabel("Multiple de 7: ");

// Ajout des composants au conteneurc.add(ln);c.add(jt);c.add(b);c.add(lm);

// Ajout de l'objet courant à la liste des écouteurs du boutonb.addActionListener(this);

}

// Implantation de la méthode d'écoutepublic void actionPerformed(ActionEvent ae){

if (ae.getSource()==b){int n = Integer.parseInt(jt.getText());if (n % 7 == 0) lm.setText("Multiple de 7: oui");else lm.setText("Multiple de 7: non");

}}

}

Signalons encore quelques petits détails. Une boîte de dialogue doit être mise en page. Cette mise enpage est réalisée grâce à un objet de la classe FlowLayout82. La mise en page que gère83 cet objetest la mise en page la plus simple. Les composants se suivent à la queue leu leu. Par exemple, lorsquele gérant ne dispose plus de suffisamment de place, vu les dimensions de la fenêtre, il dispose lessuivants en-dessous.

Pour des raisons qui tiennent plus à des adaptations qu’à de la conception pure et simple, la mise enpage ne peut s’appliquer à un objet de type JFrame, mais à un objet de type Container. C’est ce quijustifie l’emploi de la méthode getContentPane qui renvoie une référence vers le “panneau de contenu”de la boîte de dialogue84.

Page 122: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

85 Il ne suffit pas de disposer des composants dans une fenêtre pour que celle-ci soit visible. L’instruction nécessaire invoque la méthode setVisible qui prend un booléen commeparamètre. Cette méthode peut être appelée par le constructeur comme par une applicationextérieure (c’est le cas ici).

Chapitre 8 GUI et gestion des événements - 116 -

Les objets de type JButton, JTextField et JLabel, préférés aux objets de type Button, TextField etLabel ont différents constructeurs. Nous en avons utilisé l’un ou l’autre. Voyez à nouveau ladocumentation pour de plus amples informations. Ces objets qui sont des composants de la fenêtrepeuvent être ajoutés à l’objet Container associé grâce à la méthode add. Il faut aussi ajouter l’objetcourant, en l’occurrence, la boîte de dialogue elle-même, à la liste des écouteurs du bouton.

Pour les mêmes raisons, la méthode actionPerformed doit être implantée à ce niveau. Cette méthodeeffectue un simple calcul avant de modifier le label contenant le résultat du test.

Enfin, signalons que le test sur la source de l’événement n’est pas bien nécessaire puisque le bouton estle seul objet écouté par l’écouteur. Il serait utile dans la mesure où l’écouteur écoute différents objets,ce qui est évidemment tout à fait possible.

L’application qui exploite la classe venant d’être définie est relativement simple. Elle crée une boîte dedialogue et la rend visible85.

public class AppGUI{

public static void main(String [] args){

BoiteMultiple bm = new BoiteMultiple();bm.setVisible(true);

}}

Il convient de ne pas oublier les instructions d’importations nécessaires vu le nombre de classesprédéfinies utilisées.

import java.awt.*;import java.awt.event.*;import javax.swing.*;

Nous avons évoqué la possibilité de créer des objets écouteurs tout à fait indépendants des objetsgraphiques. C’est une possibilité qui donne de la souplesse à la programmation. Dans le cas qui nousoccupe, et pour des raisons que nous avons déjà évoquées (trop peu d’événements à contrôler), cettefaçon de procéder complique un peu les choses. Voici toutefois une version du programme précédentqui s’en inspire, en définissant une classe EcouteBouton. Observez que les composants graphiquesgardent l’accès package, qui est bien nécessaire à cette classe EcouteBouton.

import java.awt.*;import java.awt.event.*;import javax.swing.*;

Page 123: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 117 -

public class AppGUI1{public static void main(String [] args){

BoiteMultiple bm = new BoiteMultiple();bm.setVisible(true);EcouteBouton eb = new EcouteBouton(bm);bm.setEcouteBouton(eb);

}}

class BoiteMultiple extends JFrame {

JButton b;JTextField jt;JLabel lm;private EcouteBouton eb;

// Construction de la boîte de dialoguepublic BoiteMultiple(){

// Paramétrage de la fenêtresetTitle("Multiple de 7?");setSize(450,80);setLocation(300,300);

// Obtention d'une référence vers le conteneurContainer c = getContentPane();

// Création d'un gestionnaire de mise en page pour le conteneurFlowLayout fl = new FlowLayout();c.setLayout(fl);

// Création des composants de la fenêtreJLabel ln = new JLabel("Nombre à examiner:");jt = new JTextField(8);b = new JButton("Vérifier");lm = new JLabel("Multiple de 7: ");

// Ajout des composants au conteneurc.add(ln);c.add(jt);c.add(b);c.add(lm);

}

public void setEcouteBouton(EcouteBouton eb){

this.eb = eb;

// Ajout de l'écouteur à la liste des écouteurs du boutonb.addActionListener(eb);

}}

Page 124: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

86 Inutile de dire que cette classe implante elle-même l’interface WindowListener. Et nousretombons sur nos pattes!

Chapitre 8 GUI et gestion des événements - 118 -

class EcouteBouton implements ActionListener {

BoiteMultiple bm;

public EcouteBouton(BoiteMultiple bm){this.bm = bm;

}

// Implantation de la méthode d'écoutepublic void actionPerformed(ActionEvent ae){

if (ae.getSource()==bm.b){int n = Integer.parseInt(bm.jt.getText());if (n % 7 == 0) bm.lm.setText("Multiple de 7: oui");else bm.lm.setText("Multiple de 7: non");

}}

}

Deuxième version: contrôle de fermeture de la fenêtre

Pour éviter que le programme ne soit inutilisable après que l’utilisateur ait cliqué sur le bouton defermeture de la fenêtre, il convient de mettre un objet à l’écoute de cet événement. Ceci serait possibleen demandant à la classe de cet objet d’implanter une interface qui s’appelle WindowListener. Nousallons cependant utiliser une autre opportunité, celle d’étendre une classe, plutôt que d’implanter uneinterface. L’avantage réside dans le fait qu’il n’est pas nécessaire de redéfinir toutes les méthodes dela classe étendue (en tous cas, pas celles qui ont déjà une définition à un niveau supérieur).L’inconvénient, c’est que cette classe ne peut plus en étendre une autre. Comme ce n’est pas toujoursnécessaire, c’est au programmeur de choisir la solution qui lui convient.

La classe étendue s’appelle WindowAdapter86. Des sept méthodes que possède cette classe, nousn’en réécrirons qu’une seule, la méthode windowClosing. Nous y invoquons la méthode exit de laclasse System.

Voici (en caractères gras) les changements par rapport au code de la première application...

class EcouteFenetre extends WindowAdapter {

public void windowClosing(WindowEvent we){System.exit(0);

}}

...et au niveau de l’application elle-même...

public class AppGUI2{

Page 125: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 119 -

public static void main(String [] args){

BoiteMultiple bm = new BoiteMultiple();bm.setVisible(true);EcouteFenetre ef = new EcouteFenetre();bm.addWindowListener(ef);

}}

De la sorte, la fermeture de la fenêtre entraîne la fin du programme et l’évaporation de l’interpréteurJava.

Troisième version: classe anaonyme

A nouveau, la solution qui vient d’être proposée nous oblige à définir une classe supplémentaire. Ilexiste une possibilité de créer l’objet nécessaire “en plein vol”. Il faut pour cela utiliser la technique desclasses anonymes. Le principe est le suivant. L’objet est créé comme un objet d’une classe anonyme(héritant dans notre exemple de WindowAdapter) dont le code suit à l’intérieur même de l’instruction.

public class AppGUI3{

public static void main(String [] args){

BoiteMultiple bm = new BoiteMultiple();bm.setVisible(true);bm.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent we){System.exit(0);

}});

}}

L’écriture est un peu plus compliquée à déchiffrer mais si la classe ne sert qu’à une seule reprise, celaévite de devoir rajouter une définition de classe structurée comme à l’habitude.

Quatrième version: autre système d’écoute et traitement des exceptions

L’idée est ici de rendre l’application plus conviviale, d’une part, en n’obligeant pas l’utilisateur à cliquerinutilement sur un bouton, d’autre part, en multipliant les occasions d’effectuer la mise à jour.Concrètement:• le bouton disparaît;• la mise à jour est effectuée lorsque l’utilisateur valide le contenu du champ texte ou lorsqu’il place

le focus sur un autre élément de la boîte de dialogue.

Page 126: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 120 -

Il va s’agir ici de définir un nouveau type d’écouteurs, les “écouteurs de focus” et de faire en sorte quela boîte de dialogue soit à l’écoute des deux types d’événements. Il faudra également faire en sorte quedes données incorrectes provoquent une remise à blanc des champs de données concernés.

Voici la nouvelle définition de la classe BoiteMultiple avec mise en évidence des principauxchangements.

class BoiteMultiple extends JFrame implements ActionListener, FocusListener{

// Disparition du boutonJTextField jt;JLabel lm;

// Construction de la boîte de dialoguepublic BoiteMultiple(){

// Paramétrage de la fenêtresetTitle("Multiple de 7?");setSize(450,80);setLocation(300,300);

// Obtention d'une référence vers le conteneurContainer c = getContentPane();

// Création d'un gestionnaire de mise en page pour le conteneurFlowLayout fl = new FlowLayout();c.setLayout(fl);

// Création des composants de la fenêtreJLabel ln = new JLabel("Nombre à examiner:");jt = new JTextField(8);lm = new JLabel("Multiple de 7: ");

// Ajout des composants au conteneurc.add(new JButton("OK")); // Pour tester la perte de focusc.add(ln);c.add(jt);c.add(lm);

// Ajout de l'objet courant à la liste des écouteurs du champ textejt.addActionListener(this);jt.addFocusListener(this);

}

// Implantation des méthodes d'écoute

// Pour l'interface ActionListenerpublic void actionPerformed(ActionEvent ae){

mettreAJour();}

// Pour l'interface FocusListener

Page 127: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

87 Belle manière de transformer la réalité: il n’y a plus d’erreurs, rien que des objets quis’appellent Exception.

Chapitre 8 GUI et gestion des événements - 121 -

public void focusGained(FocusEvent fe){}

public void focusLost(FocusEvent fe){mettreAJour();

}

// Méthode de mise à jour (économie d'échelle)public void mettreAJour(){

try{int n = Integer.parseInt(jt.getText());if (n % 7 == 0) lm.setText("Multiple de 7: oui");else lm.setText("Multiple de 7: non");

}catch(NumberFormatException nfe){

jt.setText("");lm.setText("Multiple de 7: ");

}}

}

Observez que les instructions concernant le bouton ont disparu. Un bouton “bidon” a cependant étérajouté pour nous permettre de tester le programme sur la perte de focus. Sans ce bouton, il estimpossible de donner le focus à un autre objet que le champ texte.

La source d’événements est, cette fois, le champ texte avec deux événements possibles, la validationdu champ (qui génère un ActionEvent) et la perte de focus (qui génère un FocusEvent). Dans lesdeux cas, c’est le même code qui doit être exécuté, ce qui justifie la création d’une méthodemettreAJour qui permet une économie d’échelle.

La classe BoiteMultiple doit aussi implanter l’interface FocusListener. Il y a donc ici comme uneespèce d’héritage multiple. Cette interface oblige la définition des deux méthodes que sontfocusGained et focusLost. La première ne nous intéressant pas ici, nous laissons sa définition vide.Pour la seconde, nous l’avons dit, le code est le même que dans le cas d’une validation.

Le traitement des exceptions

Le test de validité de la donnée s’effectue grâce à la détection d’une exception et au moyen du bloctry... catch... dont il a déjà été question au chapitre 3. Si l’exception se produit, le bloc try estabandonné au profit du bloc catch.

Il existe évidemment un nombre considérable de types d’exceptions. Tous ces types héritent du typeException. Une des manières de les découvrir est de regarder les messages fournis par un programmequi ne rend pas les services espérés. En effet, en Java, lorsque l’on demande aux programmes deschoses impossibles, ceux-ci génèrent des objets qui sont des exceptions87. Les exceptions sont dedeux sortes, les exceptions vérifiées et les autres. Les exceptions vérifiées sont rares. Elles

Page 128: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

88 Un programme ne peut monopoliser une ressource indéfiniment (une imprimante, par exemple).

Chapitre 8 GUI et gestion des événements - 122 -

correspondent aux situations qui nécessitent une terminaison correcte du programme88. Si le code duprogramme est tel que certaines de ces exceptions peuvent se produire, le compilateur réclame auprogrammeur un traitement particulier.

La plupart des exceptions sont “non vérifiées” et le programmeur a la liberté de les traiter ou non. Pource faire, Java lui fournit la structure try... catch... Il n’est pas souhaitable de gérer tous les casd’exceptions, au risque d’alourdir considérablement les programmes. Il vaut parfois mieux réagir enfonction du fonctionnement de ce programme et prendre en compte les exceptions qui se produisentle plus fréquemment, par exemple.

Cinquième version: une mise à jour extrêmement dynamique

Et pourquoi pas demander au programme de contrôler systématiquement les modifications effectuéesdans le champ texte pour réaliser sa mise à jour?

De nouveau, il s’agit d’un changement de source d’événement. Ce n’est plus le champ texte quiconstate l’événement, c’est l’objet, de type Document, qui lui est associé. Une référence vers cetobjet peut être obtenue par la méthode getDocument de la classe JTextField. L’interface à implanterest cette fois DocumentListener et les méthodes à définir sont insertUpdate, removeUpdate etchangedUpdate. Seules les deux premières nous intéressent.

Voici à nouveau les principaux changements dans la définition de la classe BoiteMultiple.

class BoiteMultiple extends JFrame implements DocumentListener {

JTextField jt;JLabel lm;

// Construction de la boîte de dialoguepublic BoiteMultiple(){

// Paramétrage de la fenêtresetTitle("Multiple de 7?");setSize(450,80);setLocation(300,300);

// Obtention d'une référence vers le conteneurContainer c = getContentPane();

// Création d'un gestionnaire de mise en page pour le conteneurFlowLayout fl = new FlowLayout();c.setLayout(fl);

// Création des composants de la fenêtreJLabel ln = new JLabel("Nombre à examiner:");jt = new JTextField(8);lm = new JLabel("Multiple de 7: ");

Page 129: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 123 -

// Ajout des composants au conteneurc.add(ln);c.add(jt);c.add(lm);

// Ajout de l'objet courant à la liste des écouteurs du "document"// associéjt.getDocument().addDocumentListener(this);

}

// Implantation des méthodes d'écoute

// Pour l'interface DocumentListenerpublic void insertUpdate(DocumentEvent de){

mettreAJour();}

public void changedUpdate(DocumentEvent de){}

public void removeUpdate(DocumentEvent de){mettreAJour();

}

// Méthode de mise à jour (économie d'échelle)public void mettreAJour(){

try{int n = Integer.parseInt(jt.getText());if (n % 7 == 0) lm.setText("Multiple de 7: oui");else lm.setText("Multiple de 7: non");

}catch(NumberFormatException nfe){

lm.setText("Multiple de 7: ");}

}}

Le compteur interactif

Cette application va nous permettre d’utiliser des techniques d’implantation que nous connaissons déjà,mais aussi d’illustrer la possibilité et l’intérêt de créer nos propres interfaces. Elle va aussi nous donnerl’occasion d’évoquer modestement la modélisation et les systèmes de représentation liés à la POO.

Une boîte de dialogue très simple se compose d’un bouton et d’un champ texte. On souhaite que cettepetite interface graphique affiche dans le champ texte la valeur d’un compteur tout en permettantd’interagir avec lui, par l’intermédiaire du bouton.

Cette première description du problème nous amène à réfléchir séparément sur les deux objets dontil est manifestement question ici: le compteur et l’interface graphique qui permet de la contrôler.

Diagramme de classes

Page 130: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

89 Entités - Relations - Associations

90 Un constructeur, une méthode utilitaire ne seront pas nécessairement indiqués dans unschéma provisoire.

91 Il est possible de généraliser le schéma en utilisant des noms plus génériques (Bouton,ChampTexte,...)

92 Il était possible de choisir un nom plus général comme traiterEvenement, par exemple.

Chapitre 8 GUI et gestion des événements - 124 -

Compteur EcranCompteur- valeur: Entier = 0

+ incrementer()+ remettreAZero()+ getValeur(): Entier

- jt: JTextField- b: JButton- unCompteur: Compteur

+ afficher()+ setCompteur(unCompteur: Compteur)+ traiterEvenement(e: ActionEvent)

Le monde de la POO fait la part belle aux objets, décrivant les programmes et les applications commedes conversations entre ces objets. On ne s’étonnera donc pas d’apprendre que le diagramme declasses est un des diagrammes les plus fréquemment cités dans les multiples techniques de modélisationOO. Le diagramme de classes est un diagramme statique. Il se contente de décrire les classes d’objetsen présence et la manière dont les uns et les autres sont en relation. Dans les cas simples que noustraitons, les classes d’objets, et donc les relations entre elles sont assez facilement mises en évidence.Dans la pratique du génie logiciel, le diagramme des classes est généralement dérivé d’une étudesérieuse du système d’information et d’un schéma ERA89 complet.

Dans le schéma, les classes sont représentées par des rectangles. Ces rectangles contiennent, auminimum le nom de la classe, au maximum, le nom de la classe, le nom et le type d’accès des champs,la signature des méthodes, les types des éventuels résultats et les types d’accès.

Les types d’accès sont symbolisés par les signes + (public), - (private) et # (protected).

Un objet de type Compteur possède un champ valeur de type int et doit pouvoir s’augmenter, seremettre à zéro ou encore, fournir sa valeur. Un objet de type EcranCompteur doit pouvoir s’afficher,s’associer à un compteur et réagir à l’événement qui est la pression du bouton d’incrémentation. Nousaurons donc le schéma suivant:

Quelques observations sont à faire. Les classes, les champs et les méthodes qui apparaissent dans lediagramme sont ceux et celles qui jouent un rôle important90. Certains objets sont des instances declasses prédéfinies. Les noms de ces classes ont été repris avec leur véritable nom (JTextField,JButton,...)91. Il en est de même pour la méthode prédéfinie (actionPerformed92). Sans autrecontrainte, les champs sont déclarés d’accès privé et les méthodes d’accès public.

Poursuivons la réflexion. Les relations entre les classes doivent être formalisées. On distingue troistypes de relations dans un diagramme de classes:

• les relations de composition;

Page 131: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

93 Dans ce cas, le label “implements” est ajouté à la flèche.

Chapitre 8 GUI et gestion des événements - 125 -

JFrame

EcranCompteur- valeur: Entier = 0

+ incrementer()+ remettreAZero()+ getValeur(): Entier

- jt: JTextField- b: JButton- unCompteur: Compteur

+ afficher()+ setCompteur(unCompteur: Compteur)+ traiterEvenement(e: ActionEvent)

CompteurunCompteur

• les relations d’héritage;

• les relations d’utilisation.

Les relations de composition sont celles qui traduisent l’appartenance d’un objet à un autre objet. Unobjet de type EcranCompteur possède un objet de type Compteur. Cette possession se traduit auniveau des champs de données. La symbolique utilisée pour représenter cette relation est celle d’uneligne continue allant de l’objet possédé vers l’objet possesseur et se terminant par un losange plein.

Les relations d’héritage ne demandent pas d’explication. La symbolique est celle d’une flèche continuede la classe héritière vers la classe parente.

Les relations d’utilisation sont celles qui marquent les accès. Si une méthode particulière d’une classea besoin des services d’une autre classe (principalement en invoquant une de ses méthodes sur un deses objets), on dit qu’elle utilise cette classe. La relation d’utilisation est symbolisée par une flèche dontla ligne est discontinue. Cette symbolique est employée également lorsqu’une classe implante uneinterface93.

Si nous admettons que la classe EcranCompteur est sensée représenter des boîtes de dialogue pourdes incrémentations de compteurs, nous apprécierons de pouvoir bénéficier des fonctionnalités d’unetelle interface graphique (boutons de fermeture, de réduction, barre de titre,...). Cette classe hériteradonc d’une classe prédéfinie, en l’occurrence et en Java, la classe JFrame. Le diagramme s’étoffedonc un peu.

Le nom de la variable d’instance apparaît au-dessus de la ligne qui traduit la relation de composition.

Ce diagramme ne fait pas apparaître de relation d’utilisation. Si on y ajoutait une classe d’application,cette classe posséderait certainement une méthode main qui créerait des objets de type Compteur etEcranCompteur. Des relations d’utilisation associeraient cette classe application aux deux autresclasses.

Page 132: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

94 Tout ne figure pas nécessairement dans un diagramme de classes. L’implantation del’interface ActionListener et les classes Finisseur et WindowAdapter qu’elle étend ne sont pasreprésentée, de même que la classe d’application.

Chapitre 8 GUI et gestion des événements - 126 -

Première version

Voici donc une première version de cette application qui reprend bon nombre des éléments mis enévidence dans l’application précédente et qui traduit assez fidèlement le schéma qui précède94.

import java.awt.*;import java.awt.event.*;import javax.swing.*;

public class AppInterfaceGraphique{

public static void main(String [] args){

EcranCompteur ec = new EcranCompteur();Compteur c = new Compteur();ec.setCompteur(c);Finisseur f = new Finisseur();ec.addWindowListener(f);

}}

class Compteur{

private int valeur = 0;

public void incrementer(){valeur++;

}

public void remettreAZero(){valeur = 0;

}

public int getValeur(){return valeur;

}

}

/* Une interface graphique à l'écoute d'un clic sur le bouton */class EcranCompteur extends JFrame implements ActionListener {

private JButton bouton;private JTextField texte;private Container c;private FlowLayout fl;private Compteur unCompteur;

Page 133: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 127 -

// Construction de l'écranpublic EcranCompteur(){

super("Compteur");setSize(200,70);setLocation(300,200);

// Création d'un gestionnaire de mise en pagefl = new FlowLayout();c = getContentPane();c.setLayout(fl);

// Création des composantsbouton = new JButton("Augmenter");texte = new JTextField("0",4);

// Ajout des composants dans le conteneurc.add(bouton);c.add(texte);

// Ajout de l'objet courant à la liste des écouteurs du boutonbouton.addActionListener(this);

// AffichagesetVisible(true);

}

public void setCompteur(Compteur c){unCompteur = c;

}

public void actionPerformed(ActionEvent e){unCompteur.incrementer();texte.setText(Integer.toString(unCompteur.getValeur()));

}}

/* Pour le contrôle de fermeture de la fenêtre */class Finisseur extends WindowAdapter {

public void windowClosing(WindowEvent we){System.exit(0);

}}

Un diagramme de classes ne suffit évidemment pas à décrire le fonctionnement d’une application. Unprogramme étant une conversation entre des objets, cette conversation n’est évidemment pas figée.D’autres diagrammes, plutôt dynamiques ceux-là, doivent donc venir à la rescousse, pour ce qui estde décrire les interactions possibles. Un type de diagrammes très prisé pour cela est le diagramme deséquence (sequence diagram). Un diagramme de séquence traduit, sur une ligne du temps qui sedéveloppe vers le bas, quel objet est appelé, par quel autre objet et quel est celui qui possède la mainà un moment donné. Bien entendu, le nombre d’échanges et surtout les circonstances font que ces

Page 134: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

95 Imaginez, par exemple, tous les scénarios possibles lorsque vous vous rendez au distributeurde billets de banque: vous introduisez la carte à l’envers, vous introduisez une carte périmée,vous ne tapez pas le bon code, votre compte n’est pas assez approvisionné et ne peut êtredébité,...

Chapitre 8 GUI et gestion des événements - 128 -

schémas peuvent être assez nombreux. Les diagrammes de séquence traduisent donc souvent, d’abordle cas général, puis tous les cas particuliers qui peuvent se produire95.

Notez qu’en programmation, le traitement des cas particuliers est généralement plus conséquent quele traitement du cas général. La POO n’échappe pas à ça. Avant de dresser des diagrammes deséquence, l’analyste s’intéresse habituellement à ce que l’on appelle les cas d’utilisation (use cases).Ces cas sont dérivés de la connaissance que l’on a du domaine dans lequel l’application estdéveloppée. Si cette connaissance n’est pas élevée, ce sont alors des interviews des acteurs dudomaine qui permettent de mettre les choses au clair. Chaque cas d’utilisation et tous ses casparticuliers donnent lieu à des diagrammes de séquence. Ceux-ci permettent de déterminer si leprogrammeur n’a pas oublié de définir certaines méthodes au niveau de certaines classes d’objets.

On le voit, l’analyse d’une application devant déboucher sur la réalisation d’un logiciel n’est pas unemince affaire. Les quelques éléments que nous en avons donnés ne servent qu’à vous donner une idéede la complexité de la démarche. Des ouvrages spécialisés traitent de la chose. De nombreusestentatives de standardisation ont lieu à travers le développement d’UML (Uniform ModelingLanguage).

Deuxième version

Revenons à l’application pour y apporter quelques modifications, la rendre plus générale et, dans lemême temps, enrichir notre diagramme de classes.

Les principales améliorations que nous allons apporter sont les suivantes:

• utiliser une classe anonyme pour l’écoute de la fermeture de la boîte de dialogue (problème déjàtraité dans l’application précédente);

• spécialiser, par héritage, la classe Compteur;

• créer une interface (au sens de Java) pour permettre à d’autres classes d’objets de communiqueravec la classe EcranCompteur.

Voici la modification concernant l’emploi d’une classe anonyme. Elle ne demande pas decommentaires.

public class AppInterfaceGraphique2{

public static void main(String [] args){

EcranCompteur ec = new EcranCompteur();Compteur c = new Compteur();ec.setCompteur(c);

Page 135: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 129 -

JFrame

EcranCompteur- valeur: Entier = 0

+ incrementer()+ remettreAZero()+ getValeur(): Entier

- jt: JTextField- b: JButton- unCompteur: Compteur

+ afficher()+ setCompteur(unCompteur: Compteur)+ traiterEvenement(e: ActionEvent)

Compteur

- increment

+ incrementer()

CompteurVariable

unCompteur

// Contrôle de la fermeture de la fenêtreec.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent we){System.exit(0);

}});

}}

Troisième version

Un problème plus intéressant est celui qui consiste à se demander s’il ne serait pas possible de disposerd’un compteur qui puisse s’incrémenter (positivement ou négativement) avec une valeur de l’incrémentdifférente de 1. Alors qu’une programmation impérative aurait sans doute du prévoir cette situation apriori, la programmation OO offre, au travers de l’héritage, une manière élégante de solutionner leproblème.

La création d’une classe CompteurVariable qui étend la classe Compteur permet de définir descompteurs dont la valeur de l’incrément peut être fournie comme paramètre à un constructeur. Laméthode incrementer est évidemment remplacée dans la définition de cette classe.

/* Cette classe améliore la classe Compteur (héritage) */class CompteurVariable extends Compteur {

private int increment;

public CompteurVariable(int i){increment = i;

}public void incrementer(){

valeur += increment;}

}

Page 136: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 130 -

JFrame

EcranCompteur

- valeur: Entier = 0

+ incrementer()+ remettreAZero()+ getValeur(): Entier

- jt: JTextField- b: JButton- unCompteur: Compteur

+ afficher()+ setCompteur(unCompteur:GenreDeCompteur)+ traiterEvenement(e: ActionEvent)

Compteur

- increment

+ incrementer()

CompteurVariable

- valeur: Entier = 0

+ incrementer()+ remettreAZero()+ getValeur(): Entier

GenreDeCompteurInterface

implements unCompteur

Le diagramme s’étoffe à nouveau, faisant apparaître cette nouvelle relation d’héritage entreCompteurVariable et Compteur.

Quatrième version

Une autre question mérite d’être examinée. Les objets de la classe EcranCompteur possèdent unobjet de la classe Compteur. La classe d’écrans que nous avons défini ne sert donc qu’avec desobjets de type Compteur. Ceci est évidemment un peu restrictif. Imaginez que vous désiriez utiliserles mêmes écrans pour une autre classe d’objets qui possèdent aussi des méthodes d’incrémentation,de remise à zéro et de fourniture de valeur. Il faut redéfinir une nouvelle classe d’écrans dont les objetsposséderont un objet de cet autre type. Le concept d’interface permet d’éviter ce genre de problème.

Souvenez-vous: l’interface définit un contrat que les classes qui l’implantent s’engagent à respecter.C’est bien de cela qu’il s’agit ici. Les méthodes à implanter sont incrementer, remettreAZero etgetValeur. L’interface permettra une interaction de l’écran avec les objets de toute classe qui définitces méthodes.

Concrètement, il faut:

• définir une interface, appelons-la GenreDeCompteur, • préciser que la classe Compteur l’implante,• préciser que l’objet possédé par les objets de type EcranCompteur est de type

GenreDeCompteur,• modifier le type de paramètre de la méthode setCompteur.

Partant de là, il sera possible de définir d’autres classes qui implantent cette interface et utiliser la mêmeclasse d’écran avec les objets de cette nouvelle classe.

Le diagramme devient:

Page 137: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 131 -

Voici les modifications principales effectuées dans le code pour adapter la situation.

import java.awt.*;import java.awt.event.*;import javax.swing.*;

public class AppInterfaceGraphique3{

public static void main(String [] args){

EcranCompteur ec = new EcranCompteur();GenreDeCompteur c = new Compteur();ec.setCompteur(c);

// Contrôle de la fermeture de la fenêtreec.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent we){System.exit(0);

}});

}}

/* Création d'une interface en vue d'abstraire la solution */interface GenreDeCompteur{

void incrementer();void remettreAZero();int getValeur();

}

/* Cette classe implémente l'interface qui précède */class Compteur implements GenreDeCompteur {

int valeur = 0;

public void incrementer(){valeur++;

}public void remettreAZero(){

valeur = 0;}public int getValeur(){

return valeur;}

}

/* Une interface graphique à l'écoute d'un clic sur le bouton */class EcranCompteur extends JFrame implements ActionListener {

private JButton bouton;private JTextField texte;private Container c;

Page 138: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 132 -

private FlowLayout fl;private GenreDeCompteur unCompteur;

// Construction de l'écranpublic EcranCompteur(){

super("Compteur");setSize(200,70);setLocation(300,200);

// Création d'un gestionnaire de mise en pagefl = new FlowLayout();c = getContentPane();c.setLayout(fl);

// Création des composantsbouton = new JButton("Augmenter");texte = new JTextField("0",4);

// Ajout des composants dans le conteneurc.add(bouton);c.add(texte);

// Ajout de l'objet courant à la liste des écouteurs du boutonbouton.addActionListener(this);

// AffichagesetVisible(true);

}

public void setCompteur(GenreDeCompteur c){unCompteur = c;

}

public void actionPerformed(ActionEvent e){unCompteur.incrementer();texte.setText(Integer.toString(unCompteur.getValeur()));

}}

Enfin, imaginons qu’une nouvelle classe veuille pouvoir être exploitée par cette interface, il lui suffit del’implanter. En voici un exemple.

import java.awt.*;import java.awt.event.*;import javax.swing.*;

/*Pas d'importation nécessaire pour l'interface GenreDeCompteur et la classeEcranCompteur qui sont dans le même package que les classes ci-dessous.*/

public class AppInterfaceGraphique5{

Page 139: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

96 Ce qui n’est pas le cas pour une classe qui hérite d’une autre classe...

Chapitre 8 GUI et gestion des événements - 133 -

public static void main(String [] args){

EcranCompteur ec = new EcranCompteur("1");GenreDeCompteur c = new AutreSorteDeCompteur(3);ec.setCompteur(c);

// Contrôle de la fermeture de la fenêtreec.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent we){System.exit(0);

}});

}}

/* Cette autre classe implante aussi l'interface en question */class AutreSorteDeCompteur implements GenreDeCompteur {

private int valeur = 1;private int increment;

public AutreSorteDeCompteur(int i){increment = i;

}public void incrementer(){

valeur *= increment;}public void remettreAZero(){

valeur = 1;}public int getValeur(){

return valeur;}

}

Les classes internes

Pour des raisons qui tiennent plus aux contraintes d’accès qu’à une hypothétique structuration, leprogrammeur a la possibilité de définir des classes comme membres d’autres classes. En d’autrestermes, les membres d’une classe ne sont pas seulement les champs et les méthodes, mais égalementd’autres classes. Cette opportunité s’avère intéressante lorsque ces dernières sont seulement utilesdans un contexte restreint, pour des services qu’elles peuvent rendre à la classe “mère”. L’autreavantage, c’est que les champs de la classe “mère” peuvent être déclarés d’accès private et malgrétout, rester accessibles aux classes internes96. Nous exploitons cette possibilité dans la solution del’exercice qui suit. Des écouteurs d’événements sont nécessaires. Les classes de ces écouteurs sontdéfinies comme des classes internes, ce qui permet de simplifier le problème des accès aux objetsgraphiques, notamment.

Page 140: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

97 Il convient de ne pas faire l’amalgame entre la notion d’interface au sens des langages de POOet la notion d’interface graphique qui désigne ici un écran, même si ces deux notions ne sontpas complètement étrangères.

Chapitre 8 GUI et gestion des événements - 134 -

Exercice

Modifiez le programme qui simule le chronomètre en faisant afficher, toutes les secondes, le tempsécoulé dans un champ texte.

Solution

Voici une solution qui utilise plusieurs classes pour implanter les écouteurs. Ces classes sont des classesinternes.

Une classe Chronometre représente l’interface graphique qui est donc à construire. Cette interface97

graphique comprend deux zones disposées verticalement. La première zone contient un label et unezone de texte pour l’affichage du temps qui s’écoule, la seconde comprend trois boutons: un pour lelancement, un pour l’arrêt et un pour la remise à zéro du chrono.

Cette classe contient également un champ arret qui détecte si le chrono doit continuer à fonctionnermalgré l’arrêt de l’affichage dynamique.

Construction de l’interface

class Chronometre extends JFrame{

private Timer t;private JTextField jt;private JButton b1, b2, b3;

// Variable d'état: le chrono a-t-il été arrêté?

private boolean arret = false;

// Constructeur

public Chronometre(){

super("Chronomètre");Container c = getContentPane();

// Division verticale (2 cellules)

c.setLayout(new GridLayout(2,1));

// Première cellule (une étiquette et une zone de texte)

JPanel p1 = new JPanel();p1.setLayout(new FlowLayout());

Page 141: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 135 -

p1.add(new JLabel("Temps écoulé: "));jt = new JTextField("0",3);p1.add(jt);c.add(p1);

// Deuxième cellule (trois boutons)

JPanel p2 = new JPanel();p2.setLayout(new FlowLayout());b1 = new JButton("(Re)Lancer");b2 = new JButton("Arrêter");b3 = new JButton("Remettre à zéro");b2.setEnabled(false);b3.setEnabled(false);p2.add(b1);p2.add(b2);p2.add(b3);c.add(p2);

// Dimensionnement, positionnement et affichage du cadre

setSize(350,100);setLocation(200,200);setVisible(true);

// Définition d’un objet écouteur

ChangeChrono change = new ChangeChrono();

// Association des objets écouteurs aux sources

b1.addActionListener(change);b2.addActionListener(change);b3.addActionListener(change);ActionListener at = new AfficheurDeTemps();t = new Timer(1000,at);

}...

La définition n’est pas terminée puisque les classes d’écouteurs vont être définies comme des membresde cette classe. Ce sont des classes dites internes. Elles risquent d’être peu utiles ailleurs.L’avantage est évidemment que les classes internes ont accès aux membres de la classe dont elles fontpartie. Ainsi, les boutons déclarés private dans Chronometre sont accessibles dans les classesinternes.

Un objet de la classe ChangeChrono est à l’écoute des différents boutons. Cette classe est définiecomme classe interne, ce qui lui permet d’accéder aux boutons de l’interface. Cet accès est nécessairepour le test concernant la source de l’événement. Les boutons sont activés ou désactivés selon lescirconstances (méthode setEnabled). Un nouveau Timer est créé lorsque le bouton Lancer est activé,mais seulement dans le cas où le chrono n’a pas été arrêté. Dans ce dernier cas, le chrono reprendcomme s’il ne s’était pas arrêté.

Page 142: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 8 GUI et gestion des événements - 136 -

private class ChangeChrono implements ActionListener{

public void actionPerformed(ActionEvent ae){

// Le bouton "(re)lancer" est pressé.

if(ae.getSource() == b1){

// Accessibilité des boutons

b1.setEnabled(false);b2.setEnabled(true);b3.setEnabled(true);

// Pas de nouveau Timer si l'ancien a juste été arrêté.

if(!AppLayout.arret){t = new Timer(1000,new AfficheurDeTemps());

}t.start();

}

// Le bouton "arrêter" est pressé.

if(ae.getSource() == b2){

AppLayout.arret = true;t.stop();

// Accessibilité des boutons

b1.setEnabled(true);b2.setEnabled(false);

}

// Le bouton "remettre à zéro" est pressé.

if(ae.getSource() == b3){

AppLayout.arret = false;t.stop();jt.setText("0");

// Accessibilité des boutons

b1.setEnabled(true);b2.setEnabled(false);b3.setEnabled(false);

}}

}

Page 143: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

98 Vous retrouverez la justification de ces instructions au chapitre précédent, dans la premièreversion de ce programme.

Chapitre 8 GUI et gestion des événements - 137 -

Enfin, la classe AfficheurdeTemps n’a pas changé. Un de ses objets est à l’écoute du Timer quicontrôle l’écoulement du temps. Son code est repris tel quel, mais également sous forme d’une classeinterne.

private class AfficheurDeTemps implements ActionListener{

private final Date topChrono = new Date();

public void actionPerformed(ActionEvent e){

Date now = new Date();jt.setText((now.getTime() - topChrono.getTime())/1000 + " ");

}}

Il reste a fournir une classe application qui construit un objet Chronometre et ajoute à la liste de sesécouteurs, un écouteur anonyme pour la fermeture de la fenêtre. On pense aussi à fournir toutes lesinstructions d’importation nécessaires à cause de l’utilisation de plusieurs classes et interfacesprédéfinies98.

import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.Timer;import java.util.Date;

public class AppLayout{

public static void main(String [] args){

Chronometre chrono = new Chronometre();chrono.addWindowListener(new WindowAdapter(){public voidwindowClosing(WindowEvent we){System.exit(0);}});

}}

Page 144: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

99 On parle de langage de script “client-side”.

Chapitre 9 JavaScript, Java: quels rapports? - 138 -

Chapitre 9 JavaScript, Java: quels rapports?

Un dilemme

A l’heure où les cours de programmation réinvestissent dans le calme les classes de l’enseignementsecondaire, la question du choix d’un langage d’approche de la programmation orientée objet avec desétudiants se pose de manière cruciale et est sans doute controversée. En gros, deux voies sontpossibles: celle de l’utilisation d’un langage véritablement imprégné du paradigme objet (Java, C++,...)et qui fera la part belle à la créativité et à l’abstraction, ou celle d’un langage de script (JavaScript,Python, Php,...) qui se fondera davantage sur l’idée que le Web constitue une source de motivationrelativement forte à programmer. Sans prendre immédiatement parti pour l’une ou l’autre voie, voiciquelques éléments distinguant Java et JavaScript qui devraient pouvoir vous permettre de choisir enmeilleure connaissance de cause entre les deux catégories d’outils. Deux exemples sont traités enJavaScript de manière à vous faire percevoir ces différences. Néanmoins, il ne s’agit pas ici dedévelopper un autre cours sur JavaScript et les éléments du langage qui seront évoqués ne le serontque de manière très parcellaire. Pour une connaissance plus approfondie de JavaScript, je vousrenvoie à la bibliographie.

Un air de famille?

La seule ressemblance des noms de ces deux langages suffit à faire émerger la question de savoir s’ilssont effectivement liés. Les spécialistes vous diront pourtant que Javascript n'est pas Java, que lesressemblances sont nombreuses mais que ces langages sont fondamentalement différents.

Essayons donc de voir pourquoi ils sont différents et aussi, pour quelles raisons on peut les confondre.

Les langages de script

Un langage de script est, par vocation, un langage simple et faiblement typé qui va permettre ladescription rapide d’un scénario se déroulant sur le Net. Il peut s’agir d’un scénario se déroulant auchargement du document, comme il peut s’agir d’un scénario répondant à la réalisation d’un événementdont le déclencheur sera très souvent l’utilisateur.

Les langages de script se distinguent par l’endroit où s’effectue leur interprétation. Deux solutions sontpossibles:

• c’est le navigateur qui interprète le script, ce qui présuppose que le script fait partie du documentHTML reçu par le navigateur99;

• le script est interprété sur le serveur Web qui génère la page à envoyer au client Web (le navigateur).

Page 145: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

100 Voici une indication trouvée sur http://www.intranetjournal.com/faqs/jsfaq/gen1.html quisemble intéressante:JavaScript is a platform-independent, event-driven, interpreted programming languagedeveloped by Netscape Communications Corp. and Sun Microsystems. Originally calledLiveScript (and still called LiveWireTM by Netscape in its compiled, server-side incarnation),JavaScript is affiliated with Sun's object-oriented programming language JavaTM primarilyas a marketing convenience. They interoperate well but are technically, functionally andbehaviorally very different.

Chapitre 9 JavaScript, Java: quels rapports? - 139 -

Un des avantages du langage de script “client-side” est qu’il est possible de détecter et d’exploiter desévénements tels la sélection d’un bouton d’option, d’un élément dans une liste, le remplissage d’unformulaire, le clic de souris sur un bouton, etc. La validation de données à envoyer à un serveur estévidemment possible.

Une de ses limites importantes (et bien compréhensible) c’est que ses scripts ne peuvent agir sur lamémoire de la machine cliente. Au mieux, il est possible d’écrire des cookies sur son disque dur.

JavaScript

En fait, Javascript est le langage de script développé par Netscape en s’inspirant pas mal du langageJava. Pour cette raison, on peut constater que de nombreuses commandes adoptent une syntaxeressemblante ou carrément identique. De même, chacun des langages fait référence à des méthodessemblables. Javascript peut apparaître comme une sorte de Java simplifié, mais les différences sontaussi d’une autre nature100. Il existe, bien entendu, plusieurs versions de JavaScript et de nombreuxlangages de script différents. Les navigateurs s’évertuent à les reconnaître.

L’idée sous-jacente au développement de Javascript , c’est de permettre aux concepteurs de pagesWeb de développer des applications Internet sous la forme d’une extension des possibilités du HTML.Une page HTML comportant de nombreux éléments, le langage permet de modifier le comportementde ceux-ci. Il est clair que les possibilités offertes se limitent à ces modifications de comportements.La finalité reste l’affichage d’une page Web, quels que soient les traitements qui s’effectuent en arrière-plan. Une conséquence de ce qui précède est que le code d’un langage de script doit nécessairementêtre inclus dans le document HTML qu’il est généralement léger et orienté objet car il fait interveniret interagir les éléments de la page Web.

Le petit exemple qui suit montre qu’un script permet d’ajouter du code HTML au contenu d’undocument de manière à ce que le navigateur puisse l’interpréter et en afficher les effets.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Ajouter du contenu à une page</title><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"><script language="JavaScript">

Page 146: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 9 JavaScript, Java: quels rapports? - 140 -

document.writeln("<H1>JavaScript</H1><HR>");document.writeln("<H3>Un script peut agir sur le contenu de la pageaffichée.<BR>");document.writeln("Dans ce cas précis, le corps de la page est vide!</H3>");document.writeln("<H2><FONT COLOR=#FF00000>N'importe quel bout de code \HTML peut être généré par le script et interprété par le \navigateur.</FONT></H2>");

</script>

</head>

<body>

</body>

</html>

Vous pouvez constater que le corps de ce document HTML est vide. Pourtant, le navigateur produitl’affichage suivant

Une première analyse de ce petit script nous permet de faire quelques observations. Le script est icidécrit dans l’en-tête du document (head). Il aurait très bien pu figurer dans le corps, l’effet aurait étésemblable. L’interpréteur exécute les commandes au moment où il les rencontre.

Page 147: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

101 Extrait de http://www.swtech.com/script/javascript/diff/:Anyone that has tried to write JavaScript code that will run and look the same acrossdifferent versions of Netscape Navigator and Microsoft Internet Explorer will know only toowell that there are some huge differences and incompatibilities!

102 Le code Javascript, peut également être lu à partir d’un fichier texte dont l’extension est .js.

Chapitre 9 JavaScript, Java: quels rapports? - 141 -

Ce sont les balises HTML <script> et </script> qui délimitent les instructions du script, la balise detête ayant comme attribut, le langage de script utilisé, ce que le navigateur doit évidemment connaître.

Nous pouvons remarquer une certaine similitude avec Java en remarquant que writeln est uneméthode qui s’applique à l’objet document avec un paramètre qui est la chaîne de caractèrescontenant le bout de code HTML à interpréter. Il n’a pas été question ici de créer une classe, ni mêmede définir la méthode. Tout se passe comme si les classes et les méthodes étaient déjà en place, et cela,sans aucun appel à un éventuel package.

A titre indicatif, le symbole \ permet de poursuivre l’instruction sur la ligne suivante sans conséquencesur son interprétation..

Pour ce qui est de JavaScript, comme l’interpréteur est côté client, le navigateur doit être configuréen conséquence. Vous pouvez être rassurés, ils le sont généralement tous par défaut. Toutefois, il fautquand même savoir que Microsoft a développé son propre langage de script appelé Jscript (benvoyons!) et que cette copie carbone de JavaScript n’est pas complètement fidèle101. Alors méfiancepuisque chaque navigateur a une interprétation du script qui lui est propre!

Lorsque l’interpréteur est côté serveur (Php, Perl, Python,...), le problème de la configuration ne sepose pas.

Java et le HTML

Quelle que soit sa souplesse et la facilité avec laquelle vous pouvez associer du code JavaScript à vospages HTML102, ce langage ne vous permet pas de développer des applications classiques. Ce n’estpas un défaut, il n’a tout simplement pas été prévu pour ça.

En revanche, Java est un véritable langage de programmation. Son développement est plutôt parallèle(et non consécutif) à celui du Web et s’il n’a pas été initialement conçu pour lui, il s’y est relativementbien adapté. Qu’en est-il donc de ses implications au niveau des applications Internet? Java peut êtreutilisé pour programmer des applets qui sont des programmes pouvant être référencés dans une pageHTML. Cela signifie que le code de ces programmes peut être téléchargé et interprété sur la machinecliente pour autant que le navigateur le supporte. Et par défaut, la réponse est souvent positive.

On distinguera donc les programmes Java de la manière suivante:

• les applications sont des programmes indépendants qui s'exécutent directement sur une machinedans un environnement Java;

Page 148: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 9 JavaScript, Java: quels rapports? - 142 -

• les applets sont des programmes qui peuvent être téléchargés par un client-Web. Dans une pageHTML les applets sont référencées entre les balises <applet> et </applet>. Le code reçu estinterprété par la machine virtuelle intégrée dans le navigateur ou une machine virtuelle externe;

• bien que la balise ne soit pas encore standardisée en HTML 4, on parle aussi de servlets pourdésigner des programmes semblables aux applets, mais qui s'exécutent sur un serveur avant deretourner les résultats au client. On appelle une servlet directement par son URL ou on l’intègredans un document HTML entre les balises <servlet> et </servlet>. Dans le second cas, le clientreçoit d’abord le contenu du document précédent la balise <servlet>. La servlet est exécutée parle serveur, puis les résultats sont envoyés avec le reste du document. Dans le monde Java, lesservlets correspondent aux scripts CGI.

Un brin de comparaison

Nous pouvons résumer les choses de la manière suivante:

Javascript JAVA

Code intégré dans le code HTML Code non intégré mais référencé dans le codeHTML (applet)

Code interprété par le navigateur Code source compilé et fourni à la JVM

Domaine d’application limité au Net Domaine de programmation peu limité

Accès immédiat aux objets du navigateur Pas d'accès aux objets du navigateur

Le code JavaScript est donc immédiatement accessible à tout qui télécharge la page qui le contient.Le code Java est compilé, ce qui le rend inaccessible au premier abord (qui a dit qu’il était possiblede décompiler? ;-).

Contrairement aux programmes Java, les programmes JavaScript ne peuvent écrire en mémoire demasse. Au rayon des différences notoires, il est encore à signaler que JavaScript n’exige pas dedéclaration des types des variables. En JavaScript, il est possible d’instancier les classes existantesmais impossible de créer de nouvelles classes. Ces classes existantes correspondent aux différentséléments liés à l’affichage d’une page Web et font partie d’une hiérarchie bien établie.

Ces quelques différences importantes suffisent à prouver qu’il existe un fossé entre les deux langages,tant au niveau des concepts qu’au niveau des objectifs et des domaines d’application.

Un autre exemple en JavaScript

Notre comparaison des deux langages manque un peu d’illustrations. Alors voici un autre exemple quijustifie ce que nous venons de dire à leur propos.

Page 149: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 9 JavaScript, Java: quels rapports? - 143 -

Saisie de nombres et calcul d’une moyenne

On se propose de traiter le problème suivant:

l’internaute fourni des valeurs entières positives et lorsqu’il fournit la valeur 999, le script lui renvoie lavaleur moyenne.

Voici le code produisant la saisie des valeurs et l’affichage du résultat dans la page Web:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Calculer une moyenne</title><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

</head>

<body>

<H1>Calcul d'une moyenne</H1><HR><BR>

<script language="JavaScript">

var somme,compteur,nombre,n,moyenne;

somme=0;compteur=0;

nombre=window.prompt("Fournissez un nombre entier positif de moins de \quatre chiffres\n(999 pour stopper)","999");

while(nombre!="999"){n=parseInt(nombre);somme+=n;compteur++;nombre=window.prompt("Fournissez un nombre entier positif de moins \de quatre chiffres\n(999 pour stopper)","999");}

moyenne=somme/compteur;document.writeln("<H3>La moyenne des nombres introduits est " +Math.round(moyenne) +".</H3>");document.writeln("<BR>Pour un autre calcul, commandez au navigateur de \recharger cette page.");

Page 150: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Chapitre 9 JavaScript, Java: quels rapports? - 144 -

</script>

</body>

</html>

Ce code produit d’abord un affichage minimum et l’ouverture d’une petite fenêtre de saisie:

Lorsque la valeur par défaut est fournie, la fenêtre de saisie se ferme et l’affichage de la page secomplète avec le résultat.

Cette application est très sommaire et ne tient pascompte de tout une série de précautions qu’ilfaudrait prendre: pas de valeur introduite, interfacepeu conviviale, etc. Son but est simplement demettre en évidence le fait qu’un langage commeJavaScript s’intéresse essentiellement aux objetsfaisant partie de l’environnement des navigateurs:

document, fenêtre,...), que son but, même s’il permet d’effectuer des calculs et des tests, est

Page 151: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

103 Enfin, ça c’est un avis très personnel!

Chapitre 9 JavaScript, Java: quels rapports? - 145 -

principalement de produire des choses qui soient interprétables par un navigateur. Il n’autorise doncpas le développement de véritables applications.

Signalons encore que de nombreuses choses sont implicites en JavaScript, telles le typage desvariables, la création d’objets,... et que les classes d’objets sont prédéfinies de même que leursméthodes. Toutes ces caractéristiques n’en font pas forcément un langage très pédagogique103 maisil est séduisant car c’est un des moyens d’introduire le dynamisme dans les pages Web.

Enfin, vous trouverez sur le Web de nombreux scripts déjà tout prêts à l’emploi. Les éditeurs HTMLeux-mêmes sont capables d’en générer, sous la pression de quelques clics de souris.

Quant à dire que programmer en JavaScript, c’est faire de la POO, il y a un pas que je n’ai guère lecourage de franchir.

Page 152: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

Bibliographie

K. ARNOLD, J. GOSLIN, D. HOLMESThe Java Programming Language (third edition)Addison-WestleyReading 2000

DEITEL et DEITELComment Programmer en Java (troisième édition)Les Editions Reynald Goulet Inc.Québec 2000

C. S. HORSTMANN et G. CORNELLAu coeur de Java2 Vol 1 Notions fondamentalesCampusPress FranceParis 2000

C. S. HORSTMANN et G. CORNELLAu coeur de Java2 Vol 2 Fonctions avancéesCampusPress FranceParis 2000

DEITEL, DEITEL & NIETOInternet & World Wide Web, How to programPrentice-HallNew Jersey 2000

C. DELANNOYProgrammer en JavaEyrollesParis 2000

C. DELANNOYExercices en JavaEyrollesParis 2001

M. LAIPenser objet avec UML et Java (2ème édition)DUNODParis 2000

M. FOWLERUMLCampusPress Le Tout en PocheParis 2001

Page 153: Programmer avec des objets

Programmer avec des objets Etienne Vandeput ©CeFIS 2002

D. BARETT, M. BROWN, D. LIVINGSTONJavaScript, DHTML & CSSCampusPress FranceParis 2000

P. CHALEAT, D. CHARNAYProgrammation HTML et JavaScriptEyrollesParis 1998

G. Van ROSSUMPython Tutorial Release 2.1F. Drake editorPythonLabs 2001

Client-Side JavaScript Guide v. 1.3Netscape Communications Corporation1999http://developer.netscape.com/docs/manuals/js/client/jsguide/ClientGuideJS13.pdf