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

Héritage, appel de constructeurs, visibilité


Exercice 1 - Gifted

Le but de ce TP est de faire deux implantations des mêmes classes, une en utilisant l'héritage et l'autre en utilisant les interfaces et le pattern-matching.
L'idée est de montrer qu'il est possible d'utiliser l'héritage, mais que c'est plus compliqué que d'utiliser les interfaces.

Pour tous le TPs, vous avez un petit code qui vous indique comment utiliser les classes et méthodes que l'on va vous demander d'écrire. Ces tests, doivent compiler et fonctionner !
static void main() {
  // Q1
  var gift = new Gift("cake", 10);
  IO.println(gift.price());  // 10

  // Q2
  //new Gift("cake", -2);  // exception
  //new Gift(null, 2);  // exception

  // Q3
  IO.println(gift);  // Gift { name: "cake", basePrice: 10 }

  // Q4
  IO.println(new Gift("cake", 5).equals(new Gift("cake", 5)));  // true
  IO.println(gift.equals("cake"));  // false

  // Q5
  var deluxe = new Deluxe("watch", 100, "a nice gift", true);

  // Q6
  IO.println(deluxe);  // Deluxe { name: "watch", basePrice: 100, ribbon: true }
  IO.println(deluxe.price());  // 215

  // Q7
  IO.println(new Deluxe("watch", 80, "", false)
  .equals(new Deluxe("watch", 80, "", false)));  // true
  IO.println(new Gift("watch", 80).equals(new Deluxe("watch", 80, "", false)));  // false
  IO.println(new Deluxe("watch", 80, "", false).equals(new Gift("watch", 80)));  // false

  // Q8
  IO.println(Main.totalPrice(List.of(gift, deluxe)));  // 225

  // Q9
  Product product1 = new Gift("teddy bear", 20);
  Product product2 = new Deluxe("deluxe bear", 20, "a nice bear", true);
  IO.println(Main.totalPrice(List.of(product1, product2)));  // 155


  // Q10
  IO.println(Product.priceOf(product1));  // 20
  IO.println(Product.priceOf(product2));  // 135
}
     

Dans le package fr.uge.giftshop, nous allons créer, incrémentalement, les classes Gift, Delux et Main.
Pour tout ce TP, les prix sont des ints.
Le code ci-dessus correspond à la méthode main de la classe Main.

Dans un premier temps, écrire une classe cadeau (Gift), pas un record, qui contient un nom (name en anglais) de type String et un prix de base (basePrice en anglais) ainsi qu'une méthode price() qui renvoie le prix.

Pour vérifier que vous avez bien écrits les préconditions pour la classe Gift, vérifier que les appels aux constructeurs marqués "Q2" lèvent bien la bonne exception.

On souhaite afficher un Gift avec un format plus lisible. Modifier votre implantation pour cela (attention, le format doit être exactement celui demandé).

On souhaite maintenant pouvoir savoir si deux cadeaux ont les mêmes valeurs en utilisant la méthode equals habituelle.
Rappel: si on appel equals avec autre chose qu'un Gift, la méthode doit renvoyée false.
Rappel 2: si on implante equals, quelle méthode doit-on aussi implanter ?
Modifier la classe en conséquence.

On souhaite créer une classe Deluxe pour les cadeaux cossus, qui ont en plus d'un nom et d'un prix de base, un message personnel (une String) ainsi qu'optionnellement un ruban (hasRibbon de type boolean). Le prix est calculé en utilisant le prix de base plus 10 fois le nombre de caractères du message personnel plus 5 s'il y a un ruban.
Comme c'est un TP sur l'héritage, Deluxe hérite de Gift.
Oui, c'est une mauvaise idée, mais le but du TP est de montrer que c'est une mauvaise idée.
Comment doit-on déclarer la classe Deluxe ?
Comment doit-on écrire le constructeur ?
Note: en Java 25, on peut mettre les tests des préconditions avant l'appel au constructeur de la super-classe.

On peut voir que le code que nous avons écrit est farci de bugs, en effet, l'affichage et la méthode price() ne font pas ce qui est demandé.
Comment doit-on corriger la classe Deluxe ?
Faites les changements qui s'impose !
Attention, il est interdit d'ajouter des méthodes publiques en plus des méthodes demandées que cela soit dans la classe Deluxe ou dans la classe Gift.

On peut se rendre compte que le equals ne fonctionne pas non plus ?
Comment doit-on corriger le problème ?
Pour simplifier une partie de l'écriture, on va déclarer la classe Deluxe final.
En quoi cela simplifie le code ?
Faire en sortes que les tests correspondant à Q7 fonctionnent.

Écrire dans la classe Main une méthode totalPrice qui prend en paramètre une liste contenant des Gift et des Deluxe et renvoie la somme des prix.
L'implantation de totalPrice doit utiliser un stream !

Commenter le code des classes Gift et Deluxe, et réécrivez celles-ci sous forme de record en introduisant une interface Product.
Modifier le code de totalPrice pour que le code de la méthode main continue de fonctionner
Pourquoi avez-vous besoin d'une interface Product ?

Enfin, au lieu d'utiliser le polymorphisme pour la méthode price, on souhaite utiliser le pattern-matching, pour cela créer dans Product une méthode statique priceOf qui utilise le pattern-matching pour calculer les prix.
On souhaite de plus, supprimer la méthode price() écrite dans deluxe, comment faire pour que le main continue de fonctionner.

Enfin, pour les plus balèzes, on ajoute dans Main une méthode bestSeller qui renvoie le nom des produits qui a fait le plus de vente.
static void main() {
  ...
  // Q12
  IO.println(Main.bestSeller(List.of(deluxe, product1, product2, product2)));  // Optional[bear]
 }
       
Dans notre exemple, "bear" a été vendu pour 290 et "watch" pour 215.