Programmation par les Objets en Java
Les classes en Java (TD3)
Najib Tounsi
(Lien permanent: http://www.mescours.ma/Java/TD/tdJava3.html
(.pdf
))
L'objectif de ce TD est de programmer une classe (Point
) et
se familiariser avec les concepts de champ public/privé, méthode et
fonctions d'accès (e.g. set/get), constructeur, méthode
héritée (equals
, égalité entre deux objets), etc.
Sommaire
Notions à voir: La classe et ses
caractéristiques (champs/méthodes, private
/public
,
static
, constructeurs, documentation), les caractéristiques
héritées de la classe Object (méthodes equals, toString).
Créer une classe Point (coordonnée x,y) avec des méthodes setX(),
setY(), getX(), getY() pour resp. affecter une
coordonnée (x ou y)
et accéder à sa valeur (x
ou y). Mettre cette classe dans
un ficher Point.java
et le compiler.
/**
* Classe Point du plan avec ses cordonnées x et y
*/
public class Point extends Object{
private int x, y;
/**
* Methode qui affecte la valeur de son paramètre
* au Point this.
*/
public void setX(int p) {
x = p;
}
public void setY(int p) {
y = p;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
};
Faire un programme test qui crée un point p,
lui affecte des coordonnées et imprime ensuite ses coordonnées.
Mettre ce programme dans un fichier TestPoint.java
dans
le même répertoire.
public class TestPoint{
static public void main(String args[]){
Point p = new Point();
p.setX (3);
p.setY (4);
System.out.println( p.getX() );
System.out.println( p.getY() );
}
};
Constater qu'on ne peut appeler aucune méthode sur un objet
Point
sans avoir initialisé par
new Point();
constructeur par défaut prédéfini en Java (hérité
de la classe Object
).
this.x
ou this.y
au lieu de x
ou y
tout court. Dans votre programme de test main, essayer d'accéder aux
champs à x, y directement par la notation p.x
et
p.y
où p
est une variable Point
. Conclusion.
Dans la classe, mettre x, y public
au
lieu de private
cette fois-ci. Conclusion. (Remettre
ensuite x, y privés.)
Note : les mots setX
et getX
etc. sont juste
des identificateurs choisis car mnémoniques et significatifs.
Point
d'autres méthodes de votre
choix, par exemple
Définir aussi une fonction public double distance (Point b)
qui calcule la distance entre le point this
et le point
b
en paramètre. Tester en calculant la distance en les
points (1,2) et (2,3).
public static double
distance (Point a, Point b)
qui calcule la distance entre les
points a et b
en paramètres. Comment utiliser cette méthode? Quelle est la différence
avec le cas 2 précédent? Quelle serait votre choix de conception
d'une fonction distance, le cas 3. ou 2. ? La fonction public public boolean equals(Object o)
,
héritée aussi de la classe Object
, permet de tester
«l'égalité» entre deux objets. Usage :
p.equals(q);
permet de tester si les deux points p et q sont égaux ou pas.
Créer, toujours dans votre programme de test, deux points p
et q et leur affecter les mêmes coordonnées. Vérifier le
résultat de la méthode equals
sur ces points.
Conclusion? (La réponse est false
, voir ci-après)
Même question, mais cette fois-ci, le point p
est initialisé normalement avec new Point(...)
et q est initialisé par
l'affectation
q = p;
Conclusion?
Réponse : en 2. on compare en fait les références
à deux objets Point
différents,
même si ayant même valeur.
En 3. on trouve true
. On compare deux références
égales, car référence à un même objet Point
.
Surcharger la méthode equals
héritée et la définir
soi-même dans la classe Point
comme suit:
public boolean equals(Point a){
return (this.x==a.x && this.y==a.y);
}
où on compare deux points par leur coordonnées. Refaire le test
précédent. Conclusion?
(Ici, l'égalité entre deux points p
et q
se fera par comparaison de leurs coordonnées, que ce soit la même
référence ou pas).
(Voir aussi en fin de TD)
docPoint
par exemple)
et faire ensuite la commande:
javadoc -d docPoint Point.java
Les fichiers nécessaires à la documentation de la classe Point
seront créés dans le répertoire donné docPoint
sous
forme de page Web. Un fichier index.html
est le point
d'entrée dans ce répertoire. Le visualiser.
Reprendre la classe Point
avec les deux nouvelles méthodes
suivantes qui sont des constructeurs.
/**
* Initialise un point à l'origine
*/
public Point(){x = 0; y = 0;}
/**
* Initialise un point à a et b)
*/
public Point(int a, int b){x = a; y = b;}
Point p = new Point();
pour déclarer et initialiser un
objet Point
à (0, 0) par défaut
Point q = new Point(5,2);
pour déclarer et initialiser
un objet Point
à (5, 2).
A noter:
Avec new Point()
il sera fait appel au constructeur
sans paramètre défini juste ci-dessus, au lieu de celui hérité
comme dans §I.1.
Important! Initialiser un
point par constructeur, e.g. Point p = new Point (2,5)
,
n'est pas la même chose que lui affecter des valeurs par les
méthodes p.setX(2)
et p.setY(5)
, même
si dans les deux cas l'objet aura la même valeur. En effet, dans
le premier cas, initialisation,
l'objet p n'existe pas avant son initialisation, alors que dans le
deuxième cas, affectation,
p est déjà créé mais ne fait que changer de valeurs.
Vérifier que si on omet le constructeur Point() sans paramètre,
c'est une erreur de compilation. En effet, à partir du moment
qu'un constructeur est déclaré, l'appel new Point(), ne cherche
pas le constructeur défaut hérité précédemment, mais celui que
l'utilisateur doit définir aussi.
Remplacer maintenant le code {x = 0; y = 0;} du constructeur défaut, par {this (0,0);}. Vérifier que le résultat final est le même. L'instruction this(0,0) est un appel fait par l'instance courante au constructeur Point(int a, int b) avec ici 0 et 0 comme paramètres.
C'est d'ailleurs la seule fois qu'un constructeur peut être appelé explicitement.
Rajouter un constructeur avec un seul paramètre (initialisation de l’abscisse) qui affecte ce paramètre à x, et 0 à y.
Ecrire le code de ce constructeur de deux façons différentes.
Vérifier qu'on peut réécrire le constructeur (défaut) par {this (0);} qui fait appel au constructeur public Point(int a) nouvellement ajouté.
Comme pour la méthode equals (Point) (cf. I.3) , on peut redéfinir la
méthode toString() héritée aussi de Object, pour convertir un
objet Point
vers une chaîne de caractères imprimable.
Exemple:
public String toString() {
return "(" + x + "," + y + ")" ;
}
Exemple d'usage:
Point q = new Point (2,5);
System.out.println(q.toString()); // imprime (2,5)
Vérifier en changeant le message d'affichage avec un autre texte
dans la méthode toString()
.
equals
public boolean equals(Point a)
,
marche quand on est sûr de comparer toujours deux objets déclarés
Point
. Or en Java, on peut écrire :
Object p = new Point (2,5);
ainsi que
Object q = new Point (2,5);
Là, si on compare p
et q
, par p.equals(q)
,
le résultat sera false
! Justement, la surcharge va faire
choisir le profile boolean equals(Object)
héritée de la
classe Object
au lieu du profile boolean equals(Point
a)
définie dans la classe Point
! Or la méthode
héritée compare les références au lieu d'autre chose. D'où le
résultat false
si p
et q
sont deux objets distincts même si ayant des coordonnées égales.
Il faut donc refaire le profile de la méthode déclarée et la réécrire :
boolean equals(Object a){
return (this.x == ((Point)a).x &&
this.y == ((Point)a).y ) ;
}
Noter (Point)a
la conversion explicite de a
vers Point
avant de comparer.
Hum...! Mais alors, rien
n'empêche d'appeler equals
avec n'importe quel objet en
paramètre a
, par exemple p.equals("toto");
Le compilateur ne relèvera pas l'erreur à la compilation. L'erreur sera
détectée à l'exécution "java.lang.String cannot be cast to Point...
",
lors de la conversion ((Point)a)
.
Note : On reparlera de cet aspect lors de l'héritage. En
fait, ici on a redéfini la méthode equals
héritée de la
classe Object
pour la remplacer (override) dans la
classe Point
par celle donnée ci-dessus.
Evaluation
Certains exercices seront évalués à la demande et corrigés par l'encadrant du TP.