:: Enseignements :: Licence :: L3 :: 2025-2026 :: Programmation Objet avec Java ::
[LOGO]

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.