🏗️ Le cours : la programmation orientée objet
La programmation orientée objet (POO) organise un programme autour d'objets : chacun regroupe des données (ses attributs) et les actions possibles sur ces données (ses méthodes). Java est construit autour de cette idée : tout le code vit dans des classes.
Classe et objet
Une classe est un plan (comme les plans d'une maison) ; un objet est une réalisation de ce plan. À partir d'une seule classe Eleve, on peut créer autant d'objets que l'on veut, chacun avec ses propres valeurs d'attributs. new Eleve(…) réserve la mémoire, puis appelle le constructeur.
Attributs, méthodes et encapsulation
On déclare les attributs private : seul le code de la classe peut y toucher. L'extérieur passe par des méthodes qui vérifient les règles (un solde ne peut pas devenir négatif, un âge ne peut pas être de −5). C'est l'encapsulation : l'objet protège ses données.
class Compte {
private double solde;
Compte(double soldeInitial) {
solde = soldeInitial;
}
void deposer(double montant) {
solde += montant;
}
boolean retirer(double montant) {
if (montant > solde) {
return false; // refusé : solde insuffisant
}
solde -= montant;
return true;
}
double getSolde() {
return solde;
}
}
public class Banque {
public static void main(String[] args) {
Compte c = new Compte(100);
c.deposer(50);
System.out.println(c.retirer(500));
System.out.println(c.retirer(30));
System.out.println(c.getSolde());
}
}
Affichagefalse
true
120.0
Les méthodes qui lisent un attribut s'appellent des accesseurs (*getters*, getSolde()) ; celles qui le modifient des mutateurs (*setters*). On n'en écrit que pour les attributs qui doivent vraiment être accessibles.
| Visibilité | Mot-clé | Accessible depuis |
| privée | private | la classe seulement |
| paquetage | (rien) | les classes du même paquetage |
| protégée | protected | le paquetage et les classes filles |
| publique | public | partout |
Le constructeur et this
Le constructeur porte le nom de la classe et n'a pas de type de retour. Il initialise les attributs. Dans this.nom = nom;, this.nom désigne l'attribut et nom le paramètre : this est l'objet en cours. Si tu n'écris aucun constructeur, Java en fournit un sans paramètre, qui disparaît dès que tu en écris un.
static : ce qui appartient à la classe
Un attribut ou une méthode static n'appartient pas à un objet mais à la classe : il y en a un seul exemplaire, partagé. On l'appelle avec le nom de la classe (Eleve.getTotal(), Math.max(a, b)).
class Eleve {
private static int total = 0; // un seul exemplaire pour toute la classe
private String nom;
Eleve(String nom) {
this.nom = nom;
total++;
}
static int getTotal() {
return total;
}
}
public class Inscription {
public static void main(String[] args) {
new Eleve("Lina");
new Eleve("Sami");
System.out.println(Eleve.getTotal());
}
}
toString et equals
println(objet) appelle la méthode toString() de l'objet : sans redéfinition, elle affiche un code peu parlant (Point@1b6d3586). == compare les références ; pour comparer le contenu, on redéfinit equals (en pratique, on redéfinit aussi hashCode pour que les objets fonctionnent dans un HashSet).
class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
@Override
public boolean equals(Object autre) {
if (!(autre instanceof Point)) {
return false;
}
Point p = (Point) autre;
return x == p.x && y == p.y;
}
}
public class Points {
public static void main(String[] args) {
Point a = new Point(1, 2);
Point b = new Point(1, 2);
System.out.println(a);
System.out.println(a == b);
System.out.println(a.equals(b));
}
}
Affichage(1, 2)
false
true
L'héritage
class Chien extends Animal signifie « un chien est un animal » : Chien reçoit les attributs et méthodes d'Animal. Le constructeur de la classe fille commence par super(…), qui appelle celui du parent. Un attribut protected reste invisible de l'extérieur mais accessible aux classes filles. @Override demande au compilateur de vérifier que la méthode redéfinit bien une méthode du parent (il refuse en cas de faute de frappe).
Le polymorphisme
Une variable de type Animal peut contenir un Chien, un Chat ou un Animal. Quand on appelle a.crier(), c'est la version de l'objet réellement créé qui s'exécute, pas celle du type de la variable : c'est ce qu'on observe dans l'exemple de la deuxième carte. On peut ainsi écrire un seul traitement pour tous les animaux, présents et futurs.
Interfaces et classes abstraites
Une interface décrit ce qu'une classe sait faire (des méthodes sans corps) sans dire comment. Chaque classe qui l'implements doit fournir ces méthodes. Une classe abstraite (abstract class) est une classe qu'on ne peut pas instancier directement et qui peut contenir des méthodes à compléter par ses filles.
interface Forme {
double aire();
}
class Rectangle implements Forme {
private final double largeur;
private final double hauteur;
Rectangle(double largeur, double hauteur) {
this.largeur = largeur;
this.hauteur = hauteur;
}
public double aire() {
return largeur * hauteur;
}
}
class Cercle implements Forme {
private final double rayon;
Cercle(double rayon) {
this.rayon = rayon;
}
public double aire() {
return Math.PI * rayon * rayon;
}
}
public class Formes {
public static void main(String[] args) {
Forme[] formes = {new Rectangle(3, 4), new Cercle(1)};
for (Forme f : formes) {
System.out.println(f.aire());
}
}
}
Affichage12.0
3.141592653589793
Les méthodes d'une interface sont implicitement public : c'est pourquoi aire() est déclarée public dans les classes qui l'implémentent.