:: Enseignements :: Licence :: L3 :: 2025-2026 :: Programmation Objet avec Java ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
Egalité, nullabilité, mutabilité, affichage
|
Exercice 1 - Livre
Dans cet exercice, on passe en revue les constructeurs d'un record.
On cherche à écrire un record
Book représentant un livre
avec un titre et le nom de l'auteur.
Déclarer un record Book avec les composants
title et author.
Puis essayer le code suivant dans une méthode
main du record
Book.
static void main() {
var book = new Book("Da Vinci Code", "Dan Brown");
System.out.println(book.title + ' ' + book.author);
}
Expliquer.
Créer une classe Main (dans un fichier Main.java)
et déplacer le main de la classe Book
dans la classe Main.
Quel est le problème ?
Comment peut-on le corriger ?
On peut remarquer que le code permet de créer des livres ayant un titre ou un auteur
null.
var weirdBook = new Book(null, "oops");
Comment faire pour éviter ce problème sachant qu'il existe une méthode static
requireNonNull
dans la classe
java.util.Objects.
En fait, on peut simplifier le code que vous avez écrit à la question précédente en utilisant
un constructeur compact (compact constructor). Commenter le code précédent et
utiliser un constructor compact à la place.
Écrire un autre constructeur qui prend juste un titre et pas d'auteur et
ajouter un code de test dans le main.
On initialisera le champ author avec "<no author>" dans ce cas.
Comment le compilateur fait-il pour savoir quel constructeur appeler ?
On souhaite maintenant pouvoir changer le titre d'un livre déjà existant en utilisant
une méthode nommée
withTitle qui prend en paramètre le nouveau titre.
Pourquoi le code suivant ne marche pas ?
public void withTitle(String title) {
this.title = title;
}
Comment faire alors ? (indice comme
String.toUpperCase)
Écrire le code correspondant et ajouter un code de test dans le
main.
Exercice 2 - Liberté, Égalité, toString
Dans cet exercice, on s'intéresse aux méthodes automatiquement générées par le compilateur pour les records
comme equals et toString.
Qu'affiche le code ci-dessous ?
var b1 = new Book("Da Java Code", "Duke Brown");
var b2 = b1;
var b3 = new Book("Da Java Code", "Duke Brown");
System.out.println(b1 == b2);
System.out.println(b1 == b3);
Pourquoi ?
Comment faire pour tester si deux objets ont le même contenu ?
Écrire le code qui affiche si b1 et b2, puis b1 et b3
ont le même contenu.
Écrire une méthode
isFromTheSameAuthor() qui renvoie vrai si
deux livres sont du même auteur.
Et vérifier avec les deux livres suivants :
var book1 = new Book("Da Vinci Code", "Dan Brown");
var book2 = new Book("Angels & Demons", new String("Dan Brown"));
Comment faire pour que le code suivant
var javaBook = new Book("Da Java Code", "Duke Brown");
IO.println(javaBook);
affiche
Da Java Code by Duke Brown
Utiliser l'annotation @Override (java.lang.Override)
sur la méthode ajoutée à Book.
A quoi sert l'annotation @Override ?
Exercice 3 - Record vs Class
Dans cet exercice, on va chercher à récrire le code d'un record en utilisant une classe.
Cela n'a pas d'autre intérêt que de bien comprendre toutes les subtilités des records.
Voici le code produit par un étudiant pour une classe
Book :
public class Book2 {
private final String title;
private final String author;
public Book2(String title, String author) {
this.title = Objects.requireNonNull(title);
this.author = Objects.requireNonNull(author);
}
static void main() {
var book1 = new Book2("Da Vinci Code", "Dan Brown");
var book2 = new Book2("Da Vinci Code", "Dan Brown");
System.out.println(book1.equals(book2));
}
}
Malheureusement, le
main n'a pas le comportement attendu.
Quel est le problème ?
Comment corriger le problème si on s'entête à utiliser une classe ?
Ne m'oubliez pas le @Override SVP !
Exercice 4 - Jeu de rôle
Dans cet exercice, on va voir comment modéliser correctement un concept en programmation orientée objet.
On part d'un code très simple qui simule les achats d'un joueur dans un jeu de rôle.
Le joueur possède un certain nombre de pièces d'or et il peut acheter des potions de soins.
import module java.base;
public class Rogue {
private static String ask(){
while(true) {
var choice = IO.readln("Type yes (to buy), no (to skip) or quit (to leave)");
if (choice.equals("yes") || choice.equals("no") || choice.equals("quit")) {
return choice;
}
IO.println("Invalid choice, please try again.");
}
}
static void main() {
// retrieve a random number generator
var random = RandomGenerator.getDefault();
// purse contains the number of gold pieces the player has
// it is initialized with a random number between 0 and 99
var purse = random.nextInt(100);
var potions = 0;
while (true) {
IO.println("You have " + purse + " gold pieces.");
var price = random.nextInt(10);
IO.println("Do you want to buy a potion of healing for " + price + " gold pieces?");
var choice = ask();
if (choice.equals("quit")) {
break;
}
if (choice.equals("no")) {
continue;
}
// buy potion
if (purse < price) {
IO.println("You don't have enough pieces.");
continue;
}
potions++;
purse -= price;
}
IO.println("You manage to acquire " + potions + " potions with " + purse + " gold pieces remaining.");
}
}
Lire et exécuter le code ci-dessus.
On souhaite maintenant complexifier le monde du jeu.
Les prix ne seront plus en pièces d'or, mais en pièces d'or et en pièces d'argent.
Sachant que qu'il n'est pas possible de convertir des pièces d'or en pièces d'argent.
Dans un premier temps, on va abstraire la notion de prix en écrivant une classe Money
qui s'occupe de la gestion des pièces.
Quelles doivent être les méthodes de la classe Money ?
Écrire la classe Money pour ne gérer que des pièces d'or
et modifier le code du main pour qu'il utilise la classe Money.
Note: ne m'oubliez pas les préconditions !
Modifier la classe Money pour gérer les pièces d'or et les pièces d'argent.
Note: normalement, vous ne devriez pas toucher au code du main.
En fait, on veut créer des instances de Money de facon aléatoire,
donc le constructeur qui prend un int n'est pas la meilleure façon de faire,
car ela montre comment la monnaie est créée, donc son implantation.
Il nous fait abstraire la création, on pourrait avoir un constructeur qui
prend l'objet RandomGenerator en paramètre, mais c'est mal,
on ne fait pas de calcul dans un constructeur, juste des initialisations.
L'astuce habituelle consiste à écrire une méthode static fromRandom(random, max)
qui prend un paramètre un RandomGenerator et une valeur maximal et
renvoie une instance de Money.
Note: on appele ce genre de méthode des static factory methods, car leur but est
de créer des objets en faisant les calculs en dehors du constructeur.
Une bourse d'échange de monnaie vient d'ouvrir en ville, maintenant une pièce d'or coûte
12 pièces d'argent.
On peut donc avoir l'affichage suivant
You have 2 gold 4 silver pieces pieces.
Do you want to buy a potion of healing for 0 gold 6 silver pieces ?
Type yes (to buy), no (to skip) or quit (to leave)yes
You have 1 gold 10 silver pieces.
Qu'est ce que cela change en terme d'implantation ?
Note: normalement, vous ne devriez toujours pas toucher au code du
main.
© Université de Marne-la-Vallée