TP 4 — Manipulation des exceptions
Diagnostics de six fragments fautifs, division blindée jusqu'au calcul réussi, conteneurs à transvaser automatiquement — et le complément « presque-biathlon » avec trois exceptions personnalisées.
Objectif du TP : lire un fragment de code et prédire s'il compile — et sinon pourquoi —, blinder une saisie jusqu'au calcul réussi, et concevoir des exceptions personnalisées dont le traitement porte la logique métier. Le supplément synthétise le tout avec collections, enums et héritage.
1. Six fragments à diagnostiquer
Pour chaque fragment : compile-t-il, quelle erreur, comment corriger ? Verdicts :
example1appelledoIt()déclaréethrows Exceptionsanstry/catchni propagation : une checked ni traitée ni déclarée — ça ne compile pas. Correction :try/catch, outhrowsà son tour ;example2faitthrow new MyException();sans clausethrows:MyExceptiondérive d'Exception, donc checked — à déclarer dans la signature ;example3attrapeExceptionpour uneMyException: ça compile — mais le catch générique intercepte aussi toutes les autres exceptions. Bonne pratique : le type le plus précis possible ;example4retournedata, affectée seulement dans letry: sigetData()lève, la variable n'est jamais initialisée — erreur de compilation aureturn. Au passage, déclarerthrows NullPointerExceptionne sert à rien (unchecked). Correction : initialiserdata;example5appelledoItFinally()qui lève uneRuntimeException: ça compile — une unchecked n'a rien à déclarer ni attraper.doItFinallyest un faux ami : aucun blocfinallyn'intervient ;- le
mainplacecatch (RuntimeException e)avant les catch deArrayIndexOutOfBoundsExceptionetArithmeticException: ces deux types en dérivent, leurs blocs sont inatteignables — ça ne compile pas. Correction : réordonner du spécifique au générique.
2. La division blindée
Fournis : un tableau tab, une méthode division(index, divisor) qui renvoie
tab[index]/divisor, et statement() qui saisit les deux entiers via
LocalKeyboard.readInt. Trois dangers se cumulent : KeyboardException (paquetage
tpQU.tp04, levée quand autre chose qu'un entier est lu),
ArrayIndexOutOfBoundsException (index hors tableau), ArithmeticException (diviseur
zéro). Consigne : gérer les erreurs « jusqu'à ce qu'un calcul soit effectivement mené à
bout » — donc boucler :
public static void statement() {
boolean done = false;
while (!done) {
try {
int x = LocalKeyboard.readInt("Write the index of the integer to divide: ");
int y = LocalKeyboard.readInt("Write the divisor: ");
System.out.println("The result is: " + division(x, y));
done = true; // seulement si tout a réussi
} catch (KeyboardException | ArrayIndexOutOfBoundsException | ArithmeticException e) {
System.out.println("Réessayez.");
}
}
}Point clé : done = true après la division, jamais avant — sinon une saisie ratée
terminerait la boucle.
3. Conteneurs à transvaser
Des conteneurs de capacités différentes : BLISTER (1 chose), BOX (10), CRATE (50).
On peut ajouter une quantité à condition de ne pas excéder la capacité ; sinon une
exception est levée, et deux solutions : transvaser dans un conteneur plus grand, ou
l'ajout échoue.
Q1 et Q2 — le socle. L'enum Container porte la capacité (capacity, constructeur
privé, getCapacity()) ; la classe Thing (name, quantity, container) fixe sa
quantité à 0 si la quantité initiale est incompatible avec le conteneur fourni.
Q3 à Q5 — l'architecture des exceptions, qui est le vrai sujet :
ExtendedException: classe vide, qui se contente d'hériter d'Exception;setQuantity(int)privée : lèveExtendedExceptionsi l'ajout excède la capacité ;transferToLargerContainer(): transvase vers un conteneur plus grand ;add(int quantity): ajoute la quantité ou lèveExtendedException; son traitement mène au transvasement vers un conteneur plus grand — s'il en existe — puis l'ajout est effectué.
Le scénario fourni dit tout :
Thing [name=uneThing, quantity=3, container=BOX]
Thing [name=uneThing, quantity=23, container=CRATE]
Thing [name=maThing, quantity=0, container=BOX]
Impossible addition !
Thing [name=maThing, quantity=0, container=CRATE]Lecture : uneThing (3 dans une BOX) reçoit add(20) — 23 dépasse la boîte,
l'exception est traitée par transvasement vers CRATE et l'ajout aboutit. maThing
démarre à 12 dans une BOX : incompatible, quantité ramenée à 0. Son add(60) échoue
même en CRATE (60 > 50) : « Impossible addition ! » — le transvasement a bien eu lieu,
sans rollback. Q6 — commit : « tpQU-04::exo-boite », puis poussez.
4. Supplément : la course de presque-biathlon
Synthèse exceptions + collections + enum + héritage. La course peut être individuelle, par équipe ou par pays ; elle enchaîne des étapes de ski (temps de parcours) et de tir (tirs ratés convertis en pénalité temporelle propre à chaque étape). Le score le plus faible est le meilleur. Soyez rigoureux sur les signatures : les tests fournis en dépendent.
Competitor: identifiant automatique dès 0, nom, nationalité (Country), équipe (Team). Les enumsCountry— FRANCE(FR), GERMANY(GE), RUSSIA(RS), SWEDEN(SE), AUSTRIA(AT), ITALY(IT) — etTeam(TEAM_1àTEAM_3) s'affichent en version courte.toStringsous la forme<ID>-<NAME>(<COUNTRY>) -> <TEAM>,isFrom(...)(pays ou équipe),equalssur identifiant et nom.resetCpt()remet le compteur à zéro : les tests recréent des instances entre chaque test. Validez avecCompetitorTest;Stage: identifiant automatique commun à toutes les étapes.SkiingStageporte une distance,ShootingStageun nombre de tirs à réussir et une pénalité par tir raté (10 par défaut) ; affichages attendus :0(Skiing)1000met1(Shooting)5shots/penaltyPerMiss=10s. Les résultats vivent dans une table associativeCompetitor→ entier ; trois exceptions personnalisées —Exception_UnknownCompetitor(non inscrit à la course),Exception_NoResult(aucun score),Exception_InvalidRecord(déjà enregistré) — serventclear(),getNbRecord(),record(...)etgetScore(...)(surcharge sur une liste, scores additionnés). Validez avecStageTest;Race: un nom, unSet<Stage>, une table associative concurrent → numéro (le juge relève le numéro qui passe). Compteurs et indicateurs (getNbCompetitors,getNbTeams,getNbCountries,getTotalNbShots,getTotalLength),clearResults()déléguée,isValid(...)(lève et propageException_UnknownCompetitor),record(...)etgetScore(...)qui propagent les exceptions,getCompetitorScore(somme),getTeamScore/getCountryScore(moyennes endouble), classements du meilleur au moins bon (competitorRanking,teamRanking,countryRanking), etdeserterSet()— les concurrents sans résultat. Validez avecRaceTest.
Commit final : « tpQU-04::exo-biathlon », puis poussez l'ensemble.
Parce que le traitement est le cœur de l'exercice : l'exception doit apparaître dans les signatures, et le compilateur doit forcer son traitement. Une checked (extends Exception) s'impose — une unchecked pourrait passer inaperçue.
Les identifiants sont générés automatiquement et incrémentés. Sans remise à zéro entre les tests, les identifiants attendus par les assertions ne correspondent plus aux identifiants réels : c'est le prix de la répétabilité des tests.
Une étape de tir enregistre le nombre de tirs ratés ; au calcul du score, ce nombre est converti en pénalité temporelle (multiplié par la pénalité de l'étape) puis ajouté au temps de parcours. Le score global reste en secondes.
tpQU_04.pdf
R2.03 · TP 4 — Manipulation des exceptions · 3 pages
Chargement du document…
tpQU_04_sup.pdf
R2.03 · TP 4 supplément — Course de presque-biathlon · 4 pages
Chargement du document…
Dernière mise à jour le
TP 3 — Qualité de code + branches Git
PMD sur le code du R2.01, branches Git avec conflit volontaire et résolution — puis une refactorisation guidée vers une classe abstraite Student, et le planning des soutenances.
TP 5 — Patrons de conception
Une fabrique Factory sur des fractions extends Number — avec une extension aux chaînes sans aucun if — puis un inventaire récursif du parc informatique, composite avant la lettre.