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]]