Archives Wild Code School
Java

Java 06 : Programmation Orientée Objet

Quête 12 sur 43

Tu dois probablement te demander ce que l'on entend par objet dans le cadre d'un langage de programmation.

Un objet, c'est un concept (réel ou virtuel), qui possède des états et des comportements.

Cela peut, par exemple, être un canard ou une voiture :

  • un canard aura comme états son âge, son sexe, son nombre de plumes... Et comme comportements le fait de nager, faire coin-coin, dormir...
  • une voiture aura comme états sa marque, sa plaque d'immatriculation, son kilométrage... Et comme comportements le fait de rouler, de freiner, de klaxonner...

Tu vas donc découvrir comment créer et utiliser des objets en Java !

Objectifs

  • Connaître les notions liées à la programmation orientée objet
  • Apprendre les termes associés à chaque notion
  • Savoir créer ses propres objets
  • Savoir accéder aux états d'un objet et les modifier
  • Savoir ajouter et exécuter des comportements sur un objet

Classe et attributs

1. Classe

Java est un langage de programmation orienté objet (POO) : tout objet y est représenté par une classe.

Prenons l'exemple d'un canard : sa classe, pour le moment vide, se présentera de la façon suivante :

java
public class Duck {

}

2. Attribut

Un objet est défini par ses états : en Java, on appelle cela des attributs.

Un attribut se présente sous la forme suivante :

java
private type nomAttribut;

Si tu reprends l'exemple du canard, voici quelques états possibles :

java
public class Duck {

    private String name;
    private int age;
    private boolean swimming;
}

Pour le moment, ne prête pas trop attention à la visibilité private.Tu verras pourquoi tu dois l'utiliser plus tard dans la quête.

↗
What Is a Class?
docs.oracle.com
↗
What Is an Object?
docs.oracle.com
↗
La programmation orientée objet expliquée par Steve Jobs
medium.com

Constructeur, instance et référence

1. Constructeur vide

Pour construire un objet, il va te falloir ajouter un constructeur. C'est lui qui va initialiser ton objet.

Un constructeur porte le même nom que ta classe, et est défini de la manière suivante :

java
    public ClassName() {
    }

Il est possible, comme dans cet exemple, de créer un constructeur vide. Cependant, si aucun constructeur n'est défini dans ta classe, cette dernière possédera un constructeur vide par défaut.

Forcément, avec un constructeur vide, aucun attribut n'aura de valeur à l'initialisation de l'objet.

2. Constructeur explicite

Dans le cas de la classe Duck, il serait intéressant d'initialiser le nom et l'âge du canard au moment même où l'on crée un nouvel objet.

Il t'est donc possible d'appeler explicitement un constructeur qui va initialiser les attributs d'un objet.

Tu peux cumuler plusieurs constructeurs et ainsi initialiser des attributs différents lors de la création d'un objet.

java
public class Duck {

    private String name;
    private int age;
    private boolean swimming;

    public Duck(String name) {
        this.name = name;
        this.age = 0;
        this.swimming = false;
    }

    public Duck(String name, int age) {
        this.name = name;
        this.age = age;
        this.swimming = false;
    }
}

Le mot-clef this permet de cibler un attribut ou une méthode de l'objet de la classe en cours.Dans l'exemple précédent, this.age appellera donc l'attribut age de l'objet de la classe Duck.Cela permet de faire la différence entre l'argument age et l'attribut this.age, qui auraient le même nom.

3. Instance

Quand on initialise un objet d'une classe, on dit que l'on fait une nouvelle instance d'une classe.

Par exemple, tu pourras dire que tu veux créer une nouvelle instance de la classe Duck.

La notion de "nouvelle instance" se traduit par l'utilisation du mot-clef new. La création de l'instance se fait par l'appel d'un constructeur.

Lorsque tu crées une instance d'une classe, une case en mémoire va être allouée, et les attributs renseignés dans le constructeur prendront les valeurs correspondantes :

java
new Duck("Riri");

java
new Duck("Donald", 84);

4. Référence

Pour accéder à l'instance en mémoire, il faut un lien vers cette dernière : on appelle cela une référence.

En Java, cela revient à assigner l'instance à une variable, qui sera du type de la classe de l'objet :

java
Duck donald = new Duck("Donald", 84);

Ici donald est la référence d'une instance de la classe Duck.

Comme une instance est un espace en mémoire, il est possible d'avoir plusieurs références qui pointent vers une même instance :

java
Duck mysterMask = new Duck("Myster Mask");
Duck albertColvert = mysterMask;

Ici, mysterMask et albertColvert seront deux références vers une même instance. En clair, si l'un des deux est modifié, l'autre sera impacté !

↗
Creating Objects
docs.oracle.com
↗
Qu'est-ce que c'est qu'une référence en Java ?
bart-konieczny.com

Accesseurs et mutateurs

1. Accesseurs

Après la création de l'instance d'un objet, il peut être utile de récupérer la valeur d'un de ces attributs.

Créons un nouveau canard nommé "Fifi" en utilisant le constructeur Duck(String name, int age), avec son attribut age initialisé à la valeur 8.

java
Duck fifi = new Duck("Fifi", 8);

Si tu veux récupérer la valeur de son attribut age, il va falloir que tu accèdes à son attribut.

Tu serais tenté de le récupérer de la façon suivante :

java
Duck fifi = new Duck("Fifi", 8);
int age = fifi.age; // error: age has private access in Duck

Par convention, il faut donner la visibilité private aux attributs. Il est impossible de les modifier en dehors de la classe !

Une classe, c'est un peu comme une personne. Si tu veux récupérer son nom, tu vas le lui demander, tu ne vas pas fouiller son cerveau pour y trouver l'information !

Il est de la responsabilité d'une classe de partager et de mettre à jour l'état de ses attributs.

Il faut donc créer des accesseurs pour chaque attribut dont la classe autorisera l'accès. Un accesseur, appelé getter en anglais, se présente de la façon suivante :

java
public typeAttribut getAttribut() {
    return this.attribut;
}

Dans ton cas, il devient possible de récupérer l'âge en ajoutant l'accesseur suivant :

java
public int getAge() {
    return this.age;
}

Il faut ensuite appeler le getter de la façon suivante :

java
Duck fifi = new Duck("Fifi", 8);
int age = fifi.getAge(); // 8

Cas particulier de nommage des accesseurs : les booléens. Dans le cas d'un attribut de type booléen, il ne faut pas nommer l'accesseur getAttribut mais isAttribut :

java
public boolean isSwimming() {
    return this.swimming;
}

2. Mutateurs

De la même manière, si tu souhaites modifier un attribut, tu ne pourras pas le faire de la manière suivante :

java
Duck fifi = new Duck("Fifi", 8);
fifi.age = 9; // error: age has private access in Duck

Tu vas devoir définir des mutateurs, setters en anglais, qui se présentent de la façon suivante :

java
public void setAttribut(typeAttribut attribut) {
    this.attribut = attribut;
}

Tu remarques l'utilisation du type void pour le retour de la méthode. En effet, le mutateur modifie bien l'état de l'attribut, mais il ne retourne aucun résultat !

Dans ton cas, pour modifier l'attribut age :

java
public void setAge(int age) {
    this.age = age;
}

Il devient alors possible de modifier cet attribut :

java
Duck fifi = new Duck("Fifi", 8);
fifi.setAge(9); // 9;

↗
Why Should I Write Getters and Setters?
dzone.com
↗
Using Accessors and Mutators in Java
thoughtco.com

Méthodes

Ton objet contient des états et des moyens d'accéder et de modifier ces derniers. Cependant, un objet peut aussi posséder des comportements. En Java, on appelle cela des méthodes.

1. Méthode d'instance

Une méthode va te permettre de faire des traitements et d'en renvoyer le résultat. Une méthode va s'exécuter sur l'instance d'une classe, et son résultat sera différent selon l'objet sur lequel elle sera exécutée.

Tu sais déjà écrire des méthodes et tu as eu l'occasion de les revoir avec les accesseurs et mutateurs. Cependant, un exemple supplémentaire ne te fera pas de mal :

java
    public String nameAndAge() {
        return "My name is " + this.getName() + " and I'm " + String.valueOf(this.getAge());
    }

L'appel d'une méthode se fait sur l'instance de l'objet, soit :

java
    Duck loulou = new Duck("Loulou", 8);
    loulou.nameAndAge(); // My name is Loulou and I'm 8

2. Méthode statique

Tu as déjà eu l'occasion d'utiliser une méthode main(), tu as donc pu remarquer le mot clef static. Il te permet de signifier que la méthode doit être appelée directement sur une classe, et non sur l'instance d'une classe.

Une méthode statique ne te permet donc pas de récupérer l'état de ton objet. En clair, il te sera impossible d'afficher ou de modifier les attributs de la classe avec une méthode statique.

java
    public static String quack() {
        return "Quack!";
    }

Et tu appelles la méthode sur la classe :

java
    Duck.quack(); // Quack!

Une méthode statique est utile dans le cas où un comportement est le même pour tous les objets d'une classe.

↗
Using Objects
docs.oracle.com
↗
Static methods vs Instance methods in Java
geeksforgeeks.org

Récapitulatif

Cela te fait un paquet de notions à aborder. Tu vas pouvoir les tester en récupérant le dépôt git donné en ressource.

Il contient deux classes : Duck, telle qu'elle t'a été présentée dans cette quête, et Pond, que tu compileras et exécuteras, afin de voir en détail :

  • la création d'une instance de classe

  • la création d'une référence à cette instance

  • la récupération d'un attribut avec un accesseur

  • la modification de l'état d'un attribut avec un mutateur

  • l'appel d'une méthode sur une instance de la classe

  • l'appel d'une méthode statique sur la classe

  • deux références sur une même instance, et l'impact de la modification de cette dernière

Ensuite, ouvre ces deux classes pour regarder comment tout ça est organisé. N'hésite pas à les modifier afin d'ajouter de nouveaux attributs, constructeurs, getters, setters et méthodes !

↗
Dépôt git
github.com

Challenge

Recensement à la Wild Code School !

Tu vas créer une classe pour représenter de façon la plus fidèle possible tes camarades !

  1. Crée une classe Wilder
  2. Ajoute les attributs String firstname et boolean aware
  3. Crée un constructeur permettant d'initialiser firstname et aware
  4. Ajoute des getters et setters pour tous les attributs, en respectant les conventions
  5. Ajoute une méthode whoAmI() retournant le texte "Je m'appelle firstname et je suis aware" ou "Je m'appelle firstname et je ne suis pas aware", en fonction de l'état des attributs firstname et aware
  6. Crée une classe Classroom avec une méthode main()
  7. Ajoute des références vers plusieurs instances de la classe Wilder dans la méthode main() de Classroom
  8. Pour chacune des références, affiche le retour de la méthode whoAmI() dans le terminal
  9. Compile et teste Classroom : quand tout fonctionne, crée un dépôt git et envoie le tout sur GitHub
  10. Partage le lien du dépôt GitHub en solution

Exemple de résultat attendu lors de l'exécution de Classroom :

shell
$ Je m'appelle Jean-Claude et je suis aware
$ Je m'appelle Henri et je ne suis pas aware

Critères de validation

  • Le dépôt GitHub contient bien les fichiers Wilder.java et Classroom.java
  • La classe Wilder respecte les conventions de la POO : attributs privés, utilisation du mot-clef this, getters et setters nommés correctement.
  • Des références à des instances de la classe Wilder sont présentes dans la classe Classroom
  • La classe Classroom se compile sans erreur et affiche le résultat attendu dans le terminal