Ce module présente les constructions de base de [[Glossaire général#Paradigme de programmation orientée objet|programmation orientée objet ]]telles qu'elles sont proposées par le langage Java. - Classe - Attributs - Méthodes - Constructeur - Pause - Exo - Méthodes statiques - Record - Pause - Exo - Composition - TP ## Classe Une [[Glossaire général#Classe|classe]] est un [[Glossaire général#Type|type]] défini par le développeur pour représenter un concept. Une classe est composé d’un ensemble de données ([[Glossaire général#Attribut (d’une classe)|attributs]]) et de fonctions ([[Glossaire général#Méthode|méthodes]]) qui agissent dessus. ```mermaid classDiagram class Person { - name : String + Person() Person + toString() String } ``` Représentation d'une classe par un diagramme [[Glossaire général#UML|UML]] En java tout le code d'un programme doit être contenu dans une classe. Les fonctions (méthodes) ne peuvent pas être définies hors de classes Pour approfondir : - https://dev.java/learn/classes-objects/creating-classes/ - https://dev.java/learn/classes-objects/more-on-classes/ ### Attributs Les [[Glossaire général#Attribut (d’une classe)|attributs]] d’un objet ou d’une classe sont des données **encapsulées** à l'intérieur. On parle aussi de **variables d’instance** ou parfois de **propriétés** d’un objet. Pour approfondir : https://dev.java/learn/classes-objects/more-on-classes/#class-members #### `this` Les attributs qui constituent **l'état d'un objet** sont accessibles depuis les méthodes de la classe à l'aide du [[Glossaire général#Mot‑clé|mot-clé]] `this`. ```java public void recharge(int balles){ // accède et modifie un attribut d'instance // depuis une méthode d'instance this.nombreDeBalles += 2; } ``` Pour approfondir : https://dev.java/learn/classes-objects/more-on-classes/#using-this ### Instancier une classe Mot-clé `new` permet d’**[[Glossaire général#Instance / instancier|instancier]]** (de concrétiser) un classe. C’est à dire de créer un **objet** à partir du modèle que constitue la classe. Invoquer `new` sur une classe correspond à appeler un **constructeur** de celle-ci. ```java Date date = new Date(); ``` ![[class-instance-objet.gif|320x240]] Pour approfondir : https://dev.java/learn/classes-objects/creating-objects/ ## Méthode Les **[[Glossaire général#Méthode|méthodes]]** en Java sont des [[Glossaire général#Bloc|blocs]] de code qui réalisent une tâche spécifique. Elles sont définies au sein d'une [[Glossaire général#Classe|classe]] et peuvent être appelées (invoquées) pour **exécuter un comportement** particulier, souvent en utilisant les données contenues dans des **attributs** de cette classe. Pour approfondir : https://dev.java/learn/classes-objects/more-on-classes/#class-members ### Signature de méthode ![[signature-methode.png|497x185]] La **signature d'une [[Glossaire général#Méthode|méthode]]** est définie par : - Le **[[Glossaire général#Identifiant (ou nom)|nom]]** de la méthode : un identifiant unique pour la méthode dans la classe. - Le **[[Glossaire général#Type|type]] de retour** : le type de valeur que la méthode va retourner (par exemple `int`, `String`, `void` si la méthode ne retourne rien). - La **liste des paramètres** : des [[Glossaire général#Variable|variables]] passées à la méthode, permettant de fournir des données à la méthode La signature permet de différencier les méthodes dans une même classe, par exemple, grâce à la [[Glossaire général#Surcharge de méthode|surcharge]] (overloading) des méthodes. Pour approfondir : https://dev.java/learn/classes-objects/defining-methods/ ### Méthodes d'instance Ce sont des méthodes qui opèrent sur des [[Glossaire général#Instance / instancier|instances]] ([[Glossaire général#Objet|objets]]) d'une classe. Elles peuvent accéder/modifier [[Glossaire général#État d’un objet|l'état]] de l'instance. Un méthode d'instance d'une classe n'aura accès qu'aux attributs de sa propre **[[Glossaire général#Instance / instancier|instance]]** ```java class Canard{ String humeur; String couleur; public Canard(){ this.humeur="content"; this.couleur="vert"; } public quandIlVoitUnAutreCanard(){ this.humeur = "fâché"; } } ``` Ex. le constructeur initie l'état de l'objet canard à `humeur="content"` et `couleur="vert"` L'appel de la méthode `quandIlVoitUnAutreCanard()` accède et modifie l'attribut `humeur`, et l'état du de l'instance devient `humeur="fâché"` et `couleur="vert"` Pour appeler la méhode `quandIlVoitUnAutreCanard()` il est nécessaire d'instancier l'objet. --- ### Instance et paramètre de méthode Une instance d’une classe peut être passée en paramètre d’une méthode ```java public class Point { public double x; public double y; public Point(double x, double y) { this.x = x; this.y = y; } public void add(Point p) { this.x += p.x; this.y += p.y; } } ``` Ex. Une instance de Point est passée à la méthode add() ### Instance et retour de methode Une méthode peut retourner une instance d’une classe ```java public Point clone(){ return new Point(this.x, this.y) } ``` Ex. une instance de `Point` est retournée par la méthode `clone()` > [!tip] Immuabilité > Il est parfois utile de retourner des copies (voire des copies immuables) si on veut rendre une classe immuable --- ### Passage de paramètres : types primitifs En Java : - les types primitifs sont passés **par valeur** (recopie) ```java int a = 5; doubleInt(a); // 5 ``` ```java public void doubleInt (int a) { a *= 2; // le paramètre sera modifié dans la fonction mais pas a l'exterieur } ``` Pour approfondir : https://dev.java/learn/classes-objects/calling-methods-constructors/#passing-primitives ### Passage de paramètres : types référence En Java : - les types référence sont passés... **par référence** (copie de la référence) ```java public class Point { private int x; private int y; // l'instance p va être modifiée public void movePoint(Point p) { p.x += this.x; p.y += this.y; } } ``` ```java Point p1 = new Point(1,2); Point p2 = new Point(1,1); p1.movePoint(p2); p2 // x=2, y=3 ``` Pour approfondir : https://dev.java/learn/classes-objects/calling-methods-constructors/#passing-references ## Constructeur Le constructeur est une méthode particulière d’une classe qui permet d’en instancier un objet. C'est la méthode qui est invoquée quand on utilise le mot-clé `new` sur la classe. ```java Date d = new Date(); ``` ```java class Date{ Date(){ // C'est ce constructeur qui est appelé par new Date() } } ``` C’est une méthode qui porte le même nom que la classe. Elle n’a pas de valeur de retour. Une classe peut disposer de plusieurs constructeurs pour permettre des parametrages différents de celle-ci. ```java class Point { int x; int y; //Constructeur par défaut Point() { this.x = 0; this.y = 0; } Point(int x; int y) { this.x = x; this.y = y; } } ``` #### `this()` Les constructeurs d'une classe peuvent être invoqués depuis d'autres constructeurs de cette classe via `this()` ```java public class Chasseur { private int nombreDeBalles; public Chasseur(int nombreDeBalles) { this.nombreDeBalles = nombreDeBalles; } // Constructeur par défaut s'appuie sur un autre constructeur public Chasseur() { this(2); // appèle le constructeur Chasseur(int nombreDeBalles) } // ... } ``` Pour approfondir : https://dev.java/learn/classes-objects/defining-constructors/ ## Pause ![[F8D532B5-F4D5-42A7-82A6-0C66D4538052_1_105_c.jpeg|320x240]] ## Exo - Time ### 1 – Écrire une classe `Time` avec les 3 propriétés suivantes - seconds: `int` - minutes: `int` - hours: `int` Votre constructeur doit prendre en paramètres les valeurs de ces 3 propriétés ```java public Time(int hours, int minutes, int seconds) ``` --- ### 2 – Ajouter une méthode `toString()` permettant d’afficher le temps sous la forme `hh:mm:ss` ```java public String toString() ``` --- ### 3 – Ajouter une méthode permettant **d’ajouter 1 seconde** à une **instance** de `Time`. Vous devez gérer tout les cas particulier au niveau des minutes et des heures ```java public void addSecond() ``` --- ### 4 – Ajouter une méthode permettant **d’ajouter des secondes** à une **instance** de Time. Vous devez gérer tout les cas particulier au niveau des minutes et des heures ```java public void addSeconds(int seconds) ``` ### 5 - Jeu de test ```java class TestTime { public static void main(String[] args) { { Time time = new Time(0, 0, 0); time.addSeconds(1); System.out.println(time); // 00:00:01 } { Time time = new Time(0, 0, 0); time.addSeconds(2); System.out.println(time); // 00:00:02 } { Time time = new Time(0, 0, 0); time.addSeconds(60); System.out.println(time); // 00:01:00 } { Time time = new Time(0, 0, 59); time.addSeconds(60); System.out.println(time); // 00:01:59 } { Time time = new Time(0, 0, 59); time.addSeconds(61); System.out.println(time); // 00:02:00 } { Time time = new Time(0, 59, 59); time.addSeconds(1); System.out.println(time); // 01:00:00 } { Time time = new Time(23, 59, 59); time.addSeconds(1); System.out.println(time); // 00:00:00 } { Time time = new Time(23, 59, 59); time.addSeconds(2); System.out.println(time); // 00:00:01 } { Time time = new Time(0, 0, 0); time.addSeconds(3725); System.out.println(time); // 01:02:05 } { Time time = new Time(0, 0, 0); time.addSeconds(86400); System.out.println(time); // 00:00:00 } } } ``` --- ## Méthode statique Une méthode statique est une méthode qui peut être appelée sans instance. C’est l’équivalent de fonctions globales. ```java public class Vector2D { private Point p1; private Point p2; public Vector2D(Point p1, Point p2) { this.p1 = p1; this.p2 = p2; } public static Vector2D createEmptyVector() { return new Vector2D(new Point(0, 0), new Point(0, 0)); } } ``` ```java public static void main(String[] args) { Vector2D.createEmptyVector(); } ``` > [!tip] Astuce : import static >Les méthodes statiques peuvent être importées statiquement. >Cela permet de ne pas avoir à les préfixer avec le nom de classe associé. >```java >import static Vector2D.createEmptyVector; > > Vector2D v = createEmptyVector() > // équivalent à > Vector2D v = Vector2D.createEmptyVector() > ``` ## Méthodes particulières ### toString() Redéfinir la méthode `toString()` d'une classe permet de faire un affichage de l'état d'un objet **lisible par un humain**. > [!tip] Ayez la flemme ! Demandez à l'IDE > ![[idea-generate-toString.mp4]] ```java class PersonnageSansToString{ final String name; PersonnageSansToString(String name) { this.name = name; } } System.out.println(new PersonnageSansToString("Lapin")); //scratch_160$PersonnageSansToString@8efb846 ``` ```java class Personnage{ final String name; Personnage(String name) { this.name = name; } @Override public String toString() { return "Personnage{" + "name='" + name + '\'' + '}'; } } System.out.println(new Personnage("Lapin")); //Personnage{name='Lapin'} ``` ### `equals(Object o)` Quand on compare des objets avec l'opérateur `==` ce sont les références qui sont comparées. On évite donc d'utiliser `==` avec autre chose que des types primitifs. Pour comparer des objets référence entre eux, il faut redéfinir la méthode `equals(Object o)`. ```java String a = "Coda"; String b = "Coda"; String c = "Coda"; a.equals ``` > [!tip] Ayez la flemme ! Demandez à l'IDE > ![[idea-generate-equals-hashcode.mp4]] ```java public enum Cote { PILE, FACE } ``` Sans la méthode `equals(Object o) implémentée ```java class PieceSansEquals { private final Cote cote; PieceSansEquals(Cote cote) { this.cote = cote; } } PieceSansEquals p1 = new PieceSansEquals(Cote.PILE); PieceSansEquals p2 = new PieceSansEquals(Cote.PILE); PieceSansEquals p3 = new PieceSansEquals(Cote.FACE); boolean b = p1 == p2; System.out.println("p1 == p2 : " + b); // false boolean eq = p1.equals(p2); System.out.println("p1.equals(p2) : " + eq); // false boolean eq2 = p1.equals(p3); System.out.println("p1.equals(p3) : " + eq2); // false ``` Avec la méthode `equals(Object o) implémentée ```java class PieceAvecEquals { private final Cote cote; PieceAvecEquals(Cote cote) { this.cote = cote; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || obj.getClass() != this.getClass()) return false; var that = (PieceAvecEquals) obj; return Objects.equals(this.cote, that.cote); } } PieceAvecEquals p1 = new PieceAvecEquals(Cote.PILE); PieceAvecEquals p2 = new PieceAvecEquals(Cote.PILE); PieceAvecEquals p3 = new PieceAvecEquals(Cote.FACE); boolean b = p1 == p2; System.out.println("p1 == p2 : " + b); // false boolean eq = p1.equals(p2); System.out.println("p1.equals(p2) : " + eq); // true boolean eq2 = p1.equals(p3); System.out.println("p1.equals(p3) : " + eq2); // false ``` Les `record` implémentent automatiquement `equals(Object o)` ```java record PieceRecord(Cote cote) { } PieceRecord p1 = new PieceRecord(Cote.PILE); PieceRecord p2 = new PieceRecord(Cote.PILE); PieceRecord p3 = new PieceRecord(Cote.FACE); boolean b = p1 == p2; System.out.println("p1 == p2 : " + b); // false boolean eq = p1.equals(p2); System.out.println("p1.equals(p2) : " + eq); // true boolean eq2 = p1.equals(p3); System.out.println("p1.equals(p3) : " + eq2); // false ``` ### `hashcode()` `hashcode()` est une méthode qui génère un hash de votre objet en prenant en compte les valeurs des attributs que vous choisissez. C'est un peu la carte d'identité de l'objet. Ca permet à certaines Collections de distinguer des objets. Ex. si on implémente pas `hashcode()` dans nos classes, les Set ne sauront pas retirer les doublons. > [!tip] Ayez la flemme ! Demandez à l'IDE > ![[idea-generate-hashCode.mp4]] ```java class Personnage { final String name; Personnage(String name) { this.name = name; } @Override public String toString() { return name; } } Set<Personnage> set = new HashSet<>(List.of( new Personnage("Lapin"), new Personnage("Renard"), new Personnage("Canard"), new Personnage("Lapin"))); System.out.println(set); // [Lapin, Renard, Lapin, Canard] ``` ```java class PersonnageAvecHashCode { final String name; PersonnageAvecHashCode(String name) { this.name = name; } @Override public String toString() { return name; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; PersonnageAvecHashCode that = (PersonnageAvecHashCode) o; return Objects.equals(name, that.name); } @Override public int hashCode() { return Objects.hashCode(name); } } Set<PersonnageAvecHashCode> set = new HashSet<>(List.of( new PersonnageAvecHashCode("Lapin"), new PersonnageAvecHashCode("Renard"), new PersonnageAvecHashCode("Canard"), new PersonnageAvecHashCode("Lapin") )); System.out.println(set); // [Renard, Lapin, Canard] ``` ```java record PersonnageRecord(String name) { @Override public String toString() { return name; } } Set<PersonnageRecord> set = new HashSet<>(List.of( new PersonnageRecord("Lapin"), new PersonnageRecord("Renard"), new PersonnageRecord("Canard"), new PersonnageRecord("Lapin") )); System.out.println(set); // [Renard, Lapin, Canard] ``` ## Record En Java, un [[Glossaire général#Record (java)|record]] est une classe qui se déclare ainsi : ```java record Personne(Adresse adress, String name) { } ``` Leur syntaxe est particulièrement compacte et **peu verbeuse** et en fait de bons candidats pour plusieurs cas d'usages comme par exemple : - renvoyer plusieurs valeurs d'une méthode - remplacer certaines structures de données utilisées par des frameworks : tuples, paires, ... - utiliser pour transporter des données sans comportement Est équivalent à (un [[Glossaire général#Sucre syntaxique|sucre syntaxique]] pour) : ```java final class Personne { private final Adresse adress; private final String name; Personne(Adresse adress, String name) { this.adress = adress; this.name = name; } public Adresse adress() { return adress; } public String name() { return name; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || obj.getClass() != this.getClass()) return false; var that = (Personne) obj; return Objects.equals(this.adress, that.adress) && Objects.equals(this.name, that.name); } @Override public int hashCode() { return Objects.hash(adress, name); } @Override public String toString() { return "Personne[" + "adress=" + adress + ", " + "name=" + name + ']'; } } ``` > [!info]- Caractéristiques d'un record > - garantit que ses **attributs directs** sont **privés** ne peuvent pas être réaffectés (`final`) > - accesseurs pour chaques attributs : `nomAttribut()` > - pas de setters possibles > - ne peut **pas être étendue ou héritée** ( `final`) > - implémente sont **identité** (`equals()`, `hashcode()`) par la valeur de ses attributs > - implémente `toString` via l'ensemble des valeurs de ses attributs > - expose un constructeur par défaut pour l'ensemble de ses attributs ## Exo - convertir en record Reprendre le code de l'exercice sur `Time` Convertir la classe `Time` en record. Que faudrait il changer pour que cela fonctionne? Quels sont les avantages ? Quels sont les inconvénients ? ## Composition On appelle la [[Glossaire général#Composition|composition]] le fait qu’une classe utilise des **instances** d’autres classes comme **attributs**. ```mermaid classDiagram direction LR class Point { + x : double + y : double + translate(x : int, y : int) } class Vector2d { + p1 : Point + p2 : Point + add(v : Vector) } Vector2d -- Point ``` ```java public class Vector2D { private Point p1; private Point p2; public Vector2D(Point p1, Point p2) { this.p1 = p1; this.p2 = p2; } public double length() { double dx = p2.x - p1.x; double dy = p2.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } } ``` ```java public class Point { public double x; public double y; public Point(double x, double y) { this.x = x; this.y = y; } } ``` ## À suivre **TP Composition** [[TP 02 - Classes et composition]] [[Encapsulation en Java]]