On modélise ici une carte à jouer. On ira par touches successives en introduisant les concepts Java et Objet. Vous devriez pouvoir suivre le cours sans connaissances particulières en Java. Pour chaque notion, on renverra aux parties du tutoriel Oracle concerné.
On suppose que vous avez crée un nouveau projet en suivant le tutorial, donc que les paramètres de votre projet sont :
com.mco
Pour voir la fenêtre projet cliquez sur : "view » Tool Windows » project" ceci cachera le projet s'il était visible et le montrera s'il était caché. Puis pourvoir vos fichiers :
Lorsque l'on crée une nouvelle classe, il faut la placer dans le bon package. Cliquez donc toujours sur le bon package avant de créer une nouvelle classe avec le clique droit ou le menu file.
On procèdera par touches successives :
On commence par créer une classe avec uniquement des attributs et un constructeur, la v1.0 puis on ajoute les contrainte d'encapsulation des attributs.
Une fois dans le bon package on peut aussi :
package com.mco; public class Card { int value; String color; Card(int value, String color) { this.value = value; this.color = color; } }
On teste notre code :
Pour la V1, les attributs de visibilité nous permettent d'utiliser les champs directement :
package com.mco; public class Main { private static void testCreationCard() { Card septDeCoeur = new Card(7, "coeur"); System.out.println(septDeCoeur.value); System.out.println(septDeCoeur.color); } public static void main(String[] args) { testCreationCard(); } }
Le résultat devrait être :
7 coeur
Main
.
Pour changer ce qui est exécuté (par exemple si l'on a changé la classe Main de package) :
La ligne "Main class:" de l'onglet configuration doit contenir com.mco.Main
, c'est la classe qui est exécutée. Si vous avez changé la classe Main de package ou que ce n'est plus ça que vous voulez exécuter, placez le bon nom de la classe ici.
On utilise quelques règles :
public
Changer le package de Card.java
doit se faire en changeant de dossier (packages et dossiers sont identiques). Pour que cela se fasse sans douleur, on utilise les possibilités de l'IDE :
Card.java
: le code devient rouge, package et répertoire ne coïncident plusmove to package com.mco.battle
Le code ne marche plus. On le corrige avec des getters
("Code » Generate… » getter/setter").
septDeCoeur
qui seraient en fait des as de pique…
Lorsque l'on aura le choix on utilisera toujours des objets non modifiables (on dit aussi non mutables). C'est un design pattern (façon de faire) commun. Il s'appelle : value object
package com.mco.battle; public class Card { int value; String color; public Card(int value, String color) { this.value = value; this.color = color; } public int getValue() { return value; } public String getColor() { return color; } }
package com.mco; import com.mco.battle.Card; public class Main { private static void testCreationCard() { Card setDeCoeur = new Card(7, "coeur"); System.out.println(setDeCoeur.getValue()); System.out.println(setDeCoeur.getColor()); } public static void main(String[] args) { testCreationCard(); } }
Si on essaie d'afficher une carte :
System.out.println(new Card(7, "coeur"));
On obtient :
com.mco.battle.Card@511d50c0
Dès que l'on a besoin de transformer un objet en chaîne de caractères (ici la fonction System.out.println
affiche une chaîne de caractères à l'écran), Java utilise la méthode String toString()
.
Comme nous n'avons pas défini de méthode toString
, c'est celle de la classe Object qui est utilisée.
Pour en faire une nous-mêmes : "code » generate » toString"
L'annotation @Override
signifie que nous avons récrit une méthode d'une classe ancêtre. Par défaut toute nouvelle classe hérite d'Object qui définit les méthodes que toutes les classes doivent avoir.
String toString
package com.mco.battle; public class Card { int value; String color; public Card(int value, String color) { this.value = value; this.color = color; } public int getValue() { return value; } public String getColor() { return color; } @Override public String toString() { return "Card{" + "value=" + value + ", color='" + color + '\'' + '}'; } } </code java> === Main.java === <code java> // snip private static void testCreationCard() { Card setDeCoeur = new Card(7, "coeur"); System.out.println(setDeCoeur); System.out.println(setDeCoeur.getValue()); System.out.println(setDeCoeur.getColor()); } /snip
Passer les valeurs et les couleurs de la carte en paramètres ne semble pas être une bonne idée (code smell). De façon générale, pour gérer les constantes d'un type particulier et éviter de se tromper, on utilise en Java des Enum. En tous les cas, on n'utilise pas des chaînes de caractères comme on l'a fait jusqu'à présent. C'est un magic number et en code, c'est MAL.
On va le faire ici pour les couleurs, peut-être que ce sera également utile pour les valeurs, mais nous ne le ferons pas.
On se place dans le bon package puis "Clic droit » New » Java Class" Choisissez ensuite "Enum" comme type.
package com.mco.battle; public enum Colors { SPADE, HEART, DIAMOND, CLUB; @Override public String toString() { switch (this) { case SPADE: return "spade"; case HEART: return "heart"; case DIAMOND: return "diamond"; case CLUB: return "club"; default: return "other"; } } }
Après avoir changé le type de color
, on peut supprimer les getters et le toString
pour les regénérer.
package com.mco.battle; public class Card { int value; Colors color; public Card(int value, Colors color) { this.value = value; this.color = color; } public int getValue() { return value; } public Colors getColor() { return color; } @Override public String toString() { return "Card{" + "value=" + value + ", color=" + color + '}'; } }
Il n'y a qu'à la création de la carte qu'il faut toucher. Le reste fonctionne toujours.
// snip Card setDeCoeur = new Card(7, Colors.HEART); //snip
En java l'opérateur ==
sert à deux choses :
Exemple du code ci-dessous exécuté dans le main :
private static void testEqualityCard() { Card asDePique = new Card(1, Colors.SPADE); System.out.println(asDePique == asDePique); System.out.println(asDePique == new Card(1, Colors.SPADE)); }
Pour l'égalité de contenu, on utilise la méthode equals
définie dans la classe Object qu'il faut (comme toString
) regénérer ("Code » Generate » equals() and hashcode()").
On peut voir ces méthodes comme des méthodes de conversion d'objets :
String toString()
: convertit un objet en chaîne de caractères,boolean equals(Object o)
: convertit en booléen si le contenu est le même qu'un autreint hashcode()
: convertit un objet en nombre.equals
tout seul. On lui associe toujours hashcode
car deux objets égaux avec equals doivent avoir le même hashcode.
C'est à savoir si vous faites vous-même ces méthodes pour vos objets.
On laisse Intellij générer les méthodes. C'est un peu sale mais ça fait le job. Regardez http://www.ideyatech.com/effective-java-equals-and-hashcode/ qui explicite les façons de faire décrites dans ce magnifique livre qu'est effective Java.
//snip private static void testEqualityCard() { Card asDePique = new Card(1, Colors.SPADE); System.out.println(asDePique == asDePique); System.out.println(asDePique == new Card(1, Colors.SPADE)); System.out.println(asDePique.equals(new Card(1, Colors.SPADE))); System.out.println(new Card(1, Colors.SPADE).equals(asDePique)); } //snip
Moins il y a de paramètres à une méthode, mieux c'est. Uncle Bob dans son livre clean code dit que le meilleur nombre de paramètres pour une méthode est 0.
//snip public Card() { this(1, Colors.SPADE); } //snip