đ MĂ©thodes typiques dâune classe Java
Survol et attentes
Toutes les classes Java hĂ©ritent automatiquement tout ce qui est dĂ©fini dans la classe Object, mĂȘme les classes que vous Ă©crivez. Cette classe contient plusieurs mĂ©thodes utiles que les dĂ©veloppeurs tendent Ă réécrire, ce quâon appelle supplanter une mĂ©thode, pour les adapter aux nouveaux objets.
Définitions
- Surcharger une méthode
- DĂ©finir plusieurs fois une mĂ©thode avec le mĂȘme nom mais avec des paramĂštres diffĂ©rents. Java choisit la version de la mĂ©thode Ă appeler en fonction des paramĂštres passĂ©s Ă la mĂ©thode.
- Supplanter une méthode
- Redéfinir une méthode déjà définie dans une classe parente, souvent dans la classe
Object(parent de toutes les classes Java). On utilise le dĂ©corateur@Overridepour signaler explicitement quâon tente de supplanter une mĂ©thode dĂ©jĂ dĂ©finie. Des synonymes pour âsupplanterâ sont âremplacerâ ou âsurclasserâ. - Constructeur
- MĂ©thode spĂ©ciale qui initialise les objets dâune maniĂšre spĂ©cifique. Le nom dâun constructeur est le mĂȘme que le nom de la classe. Câest commun de surcharger le constructeur : un constructeur par dĂ©faut (sans paramĂštres) qui appelle le constructeur plus gĂ©nĂ©ral en lui passant des valeurs par dĂ©faut comme arguments. On utilise lâappel
this([paramÚtres])pour éviter de dupliquer le code écrit dans le constructeur plus général. - Méthode
String toString() - MĂ©thode qui produit une reprĂ©sentation textuelle de lâobjet. Cette mĂ©thode est appellĂ© automatiquement quand on tente dâafficher un objet, mais la dĂ©finition par dĂ©faut (dans
Object) affiche le nom de la classe suivi par un code hexadécimal. Il est souvent préférable de supplanter cette méthode pour afficher des informations plus utiles. - Méthodes
boolean equals(Object)etint hashCode() - La méthode
equalscompare le contenu de deux objets pour dĂ©terminer sâils sont Ă©gaux. La mĂ©thodehashCoderetourne un entier qui identifie lâobjet. La classeObjectspĂ©cifie un contrat entre ces deux mĂ©thodes : si deux objets retournent vrai avecequals, ils doivent aussi retourner le mĂȘme entier avechashCode. Il est donc important de supplanter ces deux mĂ©thodes ensemble. Les EDI modernes peuvent gĂ©nĂ©rer ces mĂ©thodes automatiquement pour vous.
Objectifs dâapprentissage
Ă la fin de cette leçon vous devrez ĂȘtre en mesure de :
- DĂ©crire le rĂŽle dâun constructeur et de la mĂ©thode
toString. - Expliquer pourquoi il faut définir les méthodes
hashCodeetequalsensemble. - Définir et distinguer surcharger une méthode et supplanter une méthode.
CritĂšres de succĂšs
- Je suis capable de surcharger des constructeurs sans dupliquer le code, notamment en utilisant lâappel
this(). - Je peux produire un
toStringqui affiche des informations utiles sur mes objets. - Je suis capable de générer les méthodes
hashCodeetequalsdans mes propres objets avec lâaide de lâEDI.
Constructeurs
On dĂ©finit une mĂ©thode spĂ©ciale qui sâappelle un constructeur afin dâinitialiser les objets dâune maniĂšre spĂ©cifique. La signature du constructeur est simplement le nom de la classe suivi par des parenthĂšses, avec ou sans paramĂštres.
class ShinyObject {
boolean isShiny;
/** Constructeur */
ShinyObject() {
isShiny = true; // initialise avec une valeur littérale
}
}
Les constructeurs sont gĂ©nĂ©ralement surchargĂ©s, câest-Ă -dire dĂ©finis plusieurs fois avec diffĂ©rents paramĂštres. On dĂ©finit souvent une version sans paramĂštres et une version avec un paramĂštre par attribut afin dâinitialiser chaque attribut dĂšs la crĂ©ation dâune instance de lâobjet.
Pour Ă©viter de dupliquer le code dans les constructeurs, câest une bonne pratique dâappeler le constructeur le plus complet dans les autres constructeurs. On le fait avec la mĂ©thode this([paramĂštres,...]).
class ShinyObject {
boolean isShiny;
/** Constructer avec paramĂštre */
ShinyObject(boolean isShiny) {
this.isShiny = isShiny; // initialise avec le paramĂštre
}
/** Constructeur sans paramĂštres */
ShinyObject() {
this(true); // passe la valeur par défaut comme argument
}
}
Le constructueur par défaut (définit dans la classe
Object) est un constructeur sans paramÚtres qui assigne la valeur par défaut aux attributs :
0pour les nombres,''pour les char,falsepour les boolean etnullpour les objets, comme lesString.
Méthode String toString()
La mĂ©thode toString produit une reprĂ©sentation textuelle de lâobjet, normalement en affichant de maniĂšre conviviale les valeurs des attributs. La dĂ©finition par dĂ©faut affiche le nom de la classe suivi par un code hexadĂ©cimal.
ShinyObject@2c7b84de
Câest dĂ©finitivement mieux de supplanter (remplacer, surclasser) cette dĂ©finition!
Reprenant la classe ShinyObject ci-dessus, on pourrait lui ajouter la méthode toString suivante :
@Override
public String toString(){
String validationTxt = isShiny ? "est" : "n'est pas"; // opérateur ternaire = if-else compact
return String.format("Cet objet %S reluisant", validationTxt);
}
Notez que la mĂ©thode retourne un String mais ne lâaffiche pas (pas de System.out.println() ni de IO.println()).
Notez aussi quâon prĂ©cĂšde la signature de la mĂ©thode avec le dĂ©corateur @Override afin de signaler plus explicitement quâon tente de supplanter une mĂ©thode dĂ©jĂ dĂ©finie dans la classe Object. Le dĂ©corateur est optionnel mais force le compilateur Ă vĂ©rifier que vous avez bien redĂ©finie une mĂ©thode existante, p. ex.: vous nâavez pas tapez tostring par accident ou mĂ©garde au lieu de toString.
Pour les valeurs dĂ©cimales, câest souvent une bonne idĂ©e dâutiliser un texte formatĂ© pour limiter le nombre de places aprĂšs la virgule Ă une prĂ©cision spĂ©cifique. Prenons lâexemple suivant :
class Student {
String name;
double average;
@Override
public String toString() {
return String.format("%s a une moyenne de %.2f", name, average);
}
}
On remarque plusieurs choses ici :
- On utilise la méthode
String String.format(String, [valeurs])pour produire le texte et non la concaténation de texte et de valeurs. - Le String intÚgre des spécifications de format directement dans le texte - le
%set le%0.2f. Ces spécifications font trois choses :- la lettre à la fin correspond à un type de données :
souSpour lesString,dpour lesint,fpour lesdoubleoufloat, etc. - les caractĂšres entre le % et la lettre donne un format optionnel. Dans le cas du
%.2f, le.2signale dâafficher seulement deux places aprĂšs la virgule. - la prĂ©sence de chaque spĂ©cification dĂ©termine combien et quels types de valeurs doivent suivre le String dans la mĂ©thode : il faut une valeur du bon type pour chaque spĂ©cification de format. Ces valeurs doivent suivre dans le mĂȘme ordre que lâordre dâapparence des spĂ©cifications dans le String.
- la lettre à la fin correspond à un type de données :
Pour plus de détails sur les spécifications et options de format possibles, voir la documentation pour la classe
Formatter.
Méthodes boolean equals(Object) et int hashCode()
La comparaison == donne vrai si les deux objets occupent le mĂȘme bloc en mĂ©moire. Ăa marche pour les valeurs primitives (int, double, char, etc.) parce que chaque nouvelle valeur primitive possible prend une nouvelle place en mĂ©moire.
Pour les objets, par contre, chaque instance occupe une nouvelle place en mĂ©moire, peu importe si les valeurs des attributs sont identiques. Câest pourquoi on utilise la mĂ©thode equals pour comparer le texte de deux Strings. En fait, il faut utiliser cette mĂ©thode pour comparer le contenu de tous les types dâobjets, pas juste les String.
La classe Object spĂ©cifie un contrat entre la mĂ©thode equals et une autre mĂ©thode, hashCode : il faut que deux instances qui retournent vrai avec le mĂ©thode equals retournent aussi le mĂȘme int avec la mĂ©thode hashCode. Ăa devient donc important de dĂ©finir les deux mĂ©thodes ensemble.
Voici un exemple (attachez vos tuques!) :
class Student {
String name;
double average;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(average);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
// clauses de garde
if (this == obj) // l'autre pointe au mĂȘme bloc de mĂ©moire
return true;
if (obj == null) // l'autre pointe Ă rien
return false;
if (getClass() != obj.getClass()) // l'autre n'est pas du mĂȘme type
return false;
// créer une instance du bon type avec l'autre Object
Square other = (Square) obj;
// comparaison des attributs des objets
if (Double.doubleToLongBits(average) != Double.doubleToLongBits(other.average))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
PremiĂšre chose Ă dire : ces deux mĂ©thodes ont Ă©tĂ© gĂ©nĂ©rĂ©es automatiquement par lâEDI VS Code. Dâautres EDI, comme Eclipse et IntelliJ, font ça aussi. Câest la façon la plus simple de les produire, surtout que câest seulement la section âComparaison des attributs des objetsâ dans la mĂ©thode equals qui pourrait (peut-ĂȘtre) nĂ©cessiter un ajustement si vous aviez envi de faire une comparaison particuliĂšre. Le reste dans equals et dans hashCode doit ĂȘtre lĂ . Et, dans ce cours, ce nâest pas nĂ©cessaire de comprendre tous les dĂ©tails.
Alors voici comment gĂ©nĂ©rer ces mĂ©thodes automatiquement avec VS Code (avec le âExtension Pack for Javaâ dĂ©veloppĂ© par Microsoft, vscjava.vscode-java-pack) :
- Cliquez sur le nom de la classe dans sa signature.
- Ici vous avez deux choix :
- Faites un clic droit sur le nom et cliquez ensuite sur lâoption âSource ActionâŠâ
- Faites la combinaison
Ctrl + .et consultez la section âMore Actionsâ
- Cliquez sur âGenerate hashCode() and equals()âŠâ
- SĂ©lectionnez les attributs Ă inclure dans leurs algorithmes et cliquez âOKâ.
- Validez le code en faisant quelque tests avec différentes instances via la classe pilote ou une classe test.
- Optionnellement, ajustez le code dans
equals()si vous avez une autre comparaison que vous préférez implémenter. Refaire les tests aprÚs toute modification.
Exercices
đ Tester la comprĂ©hension
Quiz de vĂ©rification sur les mĂ©thodes typiques dâObjects Java
đ ïž Pratique
Ăcrivez deux constructeurs pour votre objet, un sans paramĂštres qui utilise lâappel this([paramĂštres]) pour appeler le deuxiĂšme qui est dĂ©fini avec les paramĂštres nĂ©cessaires pour initialiser les attributs de lâobjet.
Supplanter les méthodes hashCode, equals et toString pour votre objet.
Testez ces nouvelles méthodes dans la classe pilote.