Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 1 Cours Java : deuxième saison ➢ Cours 7 : Exceptions, tests unitaires et assertions ➢ Cours 8 : Design Patterns 1 ➢ Cours 9 : Design Patterns 2 ➢ Cours 10 : Interfaces graphiques en Swing ➢ Cours 11 : Collections Contact: [email protected] Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 2 Exceptions, Tests Unitaires et Assertions ● Introduction aux exceptions ● Traitement des exceptions en Java ● Conception par contrat ● ● – Prérequis – Garanties Tests unitaires avec Junit – Classes de test – Méthodes de tests Erreurs logiques et assertions Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 3 Programmes robustes ● Approches formelles : ex. Floyd/Hoare – ● Approches semi­formelles : ex. Contrat – ● Assez Fiable, Moins long et coûteux Approches empiriques : ex. Junit – ● Fiable, Long, Coûteux Moins fiable, moins long et coûteux Important: approches complémentaires Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 4 Dans ce cours ● Gestion propre des erreurs en Java – ● ● Important: souvent mal fait Conception par contrat « light » – Méthode de conception – Systématise la gestion des exceptions Test unitaire avec Junit – Pour écrire proprement des tests Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 5 Typologie des erreurs ● Erreurs de compilation : par le compilateur javac – ● Erreurs de syntaxe, pbm. de typage Erreurs d'exécution : par la machine virtuelle java – Erreurs système : générée par l'environnement => RuntimeException – Erreurs contractuelles : mauvaise utilisation d'un objet => Exception – Erreurs logiques : un bug du programme ! => AssertionError Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 6 Les Exceptions ● En Java, toute erreur est une exception ● Une exception = un objet d'une classe qui ● – hérite de java.lang.RuntimeException : exceptions système, non­vérifiées par le compilateur (pas de déclarations throws) – hérite de java.lang.Exception : exceptions vérifiées par le compilateur (nécessité des déclarations throws) Règle : on ne définit nous ­même que des exception non­vérifiées, donc on héritera systématiquement de java.lang.Exception (ou d'une sous­classe) Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 7 Pourquoi les exceptions ? ● Erreurs à la construction – ● Un constructeur ne retourne rien, donc surement pas un code d'erreur Séparation des préoccupations – D'un côté : code qui génère les erreurs => throw – De l'autre côté : code qui traite les erreurs => try ... catch ● Syndrome du « segmentation fault » – Messages d'erreurs – « remontée » de la pile d'exécution – Possibilité de récupération (poursuivre malgré l'exception) Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 8 Traitement des exceptions ● Traitement immédiat – ● Clauses multiples – ● Plusieurs types d'exceptions traitées au même endroit Délégation / Filtrage – ● On traite l'exception dès qu'on la détecte, c'est le cas le plus fréquent On délègue le traitement de l'exception, même si on la détecte Clause finale – Du code exécuté quoi qu'il arrive (exception levée ou non) Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 9 Traitement immédiat Les blocs try ... catch : Public class MaClasse { .... public void maMethode(...) { try { // ici, code générant éventuellement // une exception de type MonException } catch(MonException e) { // traitement de l'exception } .... } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 10 Exemple: utilisation du JDK (1/2) Dans la doc. du jdk: FileReader public FileReader(String fileName) throws FileNotFoundException Creates a new FileReader, given the name of the file to read from. Parameters: fileName - the name of the file to read from Throws: FileNotFoundException - if the named file does not exist, is a directory rather than a regular file, or for some other reason cannot be opened for reading. Important: si on veut utiliser une méthode qui throws une exception, le programme ne compilera pas si on indique comment traiter cette exception Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 11 Exemple: utilisation du JDK (2/2) Mode d'emploi: Public class MaClasse { .... public void maMethode(...) { try { // ici, code générant éventuellement // une exception FileReader fr = new FileReader("toto.txt"); // et la suite ... } catch(FileNotFoundException e) { // traitement de l'exception System.err.println("Je ne trouve pas toto.txt"); } .... } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 12 Clauses multiples : pourquoi ? ● Problèmes – Un même instruction peut générer plusieurs types d'exception différents Exemple: constructeur de java.io.FileInputStream throws FileNotFoundException et SecurityException On veut mettre plusieurs instructions dans le corps d'un try ... catch, chaque instruction peut lever plusieurs types d'instructions ● – ● Exemple: – première instruction : constructeur FileReader peut lever FileNotFoundException – Second instruction: lecture dans le fichier avec méthode read de FileReader, peut lever IOException Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 13 Clauses multiples : comment ? Public class MaClasse { .... public void maMethode(...) { try { // ici, code générant éventuellement // une exception FileReader fr = new FileReader("toto.txt"); int carac = fr.read(); // et la suite ... } catch(FileNotFoundException e) { // traitement de l'exception System.err.println("Je ne trouve pas toto.txt"); } catch(IOException e) { // traitement de l'exception System.err.println("Problème de lecture"); } .... } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 14 Clauses multiples et héritage ● ● Les exceptions sont des objets, ils sont donc instances de classes et on peut donc hériter de classes d'exceptions L'ordre dans lequel on liste les exceptions « catch » est important – D'abord les sous­classes d'exceptions les moins génériques – Ensuite les super­classes d'exceptions les plus génériques Raison: c'est simple, si on traite d'abord un cas plus générique, on ne traitera jamais le cas le plus spécifique ● Ne pas s'inquiéter, le compilateur vérifie tout cela Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 15 Que faire dans une clause catch ? ● Au minimum (phase de développement): – Afficher un message d'erreur ● ● ● Conseil : } catch(MonException e) { System.err.println("Erreur : patati"); e.printStackTrace(System.err); } Au mieux (programme diffusé): – Récupération de l'erreur – Délégation ou filtrage : prévenir les « supérieurs » INTERDIT ! } catch(MonException e) { // ici je ne fais rien } – De ne rien faire – Pourquoi ? Parce que l'utilisateur ne sait pas qu'il s'est passé quelque chose ! Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 16 Délégation Question: que faire si on ne veut/peut pas traiter une exception ? Réponse 1: on peut déléguer à celui qui nous a appelé public class MaClasse { .... public void maMethode(...) throws FileNotFoundException, IOException { // ici, code générant éventuellement // des exceptions FileReader fr = new FileReader("toto.txt"); int carac = fr.read(); // et la suite ... ... } } Remarque: si on oublie le throws alors le compilateur se plaint Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 17 Filtrage Question: que faire si on ne veut/peut pas traiter une exception ? Réponse 2: on peut filtrer pour celui qui nous a appelé public class MaClasse { .... public void maMethode(...) throws FichierException { try { FileReader fr = new FileReader("toto.txt"); int carac = fr.read(); // et la suite ... } catch(Exception e) { // pour toute exception FichierException fe = new FichierException("problème de fichier"); fr.initCause(e); // enregistrer la cause throw fr; // lancer l'exception filtrée } } } Remarque: la classe FichierException est une exception personalisée (cf. suite du cours) Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 18 Clause finale Du code exécuté dans tous les cas, même si exception il y a public class MaClasse { .... public void maMethode(...) { try { // ici, code générant éventuellement // une exception FileReader fr = new FileReader("toto.txt"); // et la suite ... } catch(FileNotFoundException e) { // traitement de l'exception System.err.println("Je ne trouve pas toto.txt"); } finally { fr.close(); // en fait pas nécessaire en Java } ... } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 19 Exceptions et conception par contrat ● ● ● Questions rituelles sur les « exceptions » – Pourquoi créer ses propres classes d'exceptions ? – Quand et pourquoi signaler une exception personnalisée ? Réponses de la conception par contrat: – On créer une classe d'exception (ou hiérarchie) par catégorie de contrat (en général une hiérarchie par paquetage de 10 à 20 classes maxi) – On signale une exception si un contrat est rompu La conception par contrat fournit de plus: – un moyen de gérer les erreurs logiques avec les assertions – Enfin, elle sert de guide pour l'élaboration des tests unitaires Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 20 Principes de la conception par contrat ● Fournisseurs – ● Clients – ● Classes/Méthodes que l'on définit nous­même Code externe (que l'on ne voit pas) et qui utilise nos classes fournisseurs Contrats (pour chaque méthode publique d'une classe en cours de conception) : – Prérequis (ou précondition externe) : ce que les clients doivent respecter lorsqu'ils font appel au fournisseur en cours de conception. – Garanties (ou postcondition externe) : ce que le fournisseur se charge de fournir si le client respecte sa part du contrat et que tout se passe bien. Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 21 Exemple de contrat (monde réel) ● Fournisseur: – ● Clients: – ● Société de chemins de fer Passagers Contrat : transport de Paris à Marseille – Prérequis (conditions booléennes imposées au client) : ● ● – Le client paye son billet Le client arrive à l'heure pour son train Garanties (conditions booléennes vraies après traitement): ● ● Transport en tout sécurité du passager dans les délais prévus Prévoir une indeminisation en cas de retard Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 22 Exemple (monde des objets) 1/7 Une classe de Cube bornée contenant du liquide (en litres) public class CuveBornee { private double niveau; private double limite; public CuveBornee(double limite) { this.limite = limite; niveau = 0; } public double getNiveau() { return niveau; } public void remplir(double quantite) { ... } public void vider(double quantite) { ... } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 23 Exemple (monde des objets) 2/7 ● Fournisseur: – ● Clients: – ● Méthode remplir() de la classe CuveBornee Toute expression qui invoque la méthode remplir sur un objet de la classe CuveBornee depuis l'extérieur (ex.: dans une classe de test pour cuve bornée, dans un programme qui a besoin d'une cuve bornée, etc.) Contrat: remplir la cuve avec du liquide – Prérequis : le liquide versé par le client, ajouté au niveau actuel ne dépasse pas la limite – Garanties : le niveau est l'ancien niveau auquel on ajoute la quantité versée par le client Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 24 Exemple (monde des objets) 3/7 Que peut­il se passer ? ● Le client assure les prérequis – Le fournisseur fournit les garanties – Le client peut tester les garanties => Ecrire un test ● Si un test de garantie échoue, alors il s'agit d'un bug ! Un problème d'environnement survient (ex. plus de mémoire) : alors une exception système est levée par Java (nous, on ne s'en occupe pas à priori) ● ● Le client n'assure pas les prérequis – Le fournisseur lève une exception personnalisée => définir une (ou plusieurs) classe(s) personnalisée(s) Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 25 Exemple (monde des objets) 4/7 Phase 1 : Classe(s) d'exception(s) personnalisée(s) // Exception de base pour tous les problèmes de cuve public class CuveException extends Exception { public CuveException(String message) { super("Problème de cuve : " + message); } } // Exception spécifique pour le contrat de remplir() public class CuvePleineException extends CuveException { public CuvePleineException() { super("Cuve pleine"); } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 26 Exemple (monde des objets) 5/7 Phase 2 : Vérification des prérequis => tester la condition de prérequis => lever une exception personnalisée si la condition est fausse Public class CuveBornee { ... public void remplir(double liquide) throws CuvePleineException { // PREREQUIS : Le liquide versé, ajouté au niveau actuelle, // ne dépasse pas la limite if(niveau+liquide>limite) throw new CuvePleineException(); // ... la suite } ... } Rappels : Le fournisseur est la méthode remplir, Les clients sont ceux qui invoquent la méthode Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 27 Exemple (monde des objets) 6/7 Phase 3 : Description des traitements => (enfin) le code java de la méthode ! Public class CuveBornee { ... public void remplir(double liquide) throws CuvePleineException { // PREREQUIS : Le liquide versé, ajouté au niveau actuelle, // ne dépasse pas la limite if(niveau+liquide>limite) throw new CuvePleineException(); // TRAITEMENT niveau = niveau+liquide; // la suite } ... } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 28 Exemple (monde des objets) 7/7 Phase 4 : Expression des garanties => un commentaire qui permettra ensuite de créer un test public class CuveBornee { ... public void remplir(double liquide) throws CuvePleineException { // PREREQUIS : Le liquide versé, ajouté au niveau actuelle, // ne dépasse pas la limite if(niveau+liquide>limite) throw new CuvePleineException(); // TRAITEMENT niveau = niveau+liquide; // GARANTIE : le niveau est l'ancien niveau auquel on ajoute // la quantité versée par le client // this.getNiveau() = old.getNiveau()+liquide; // Problème : java ne connait pas old, donc commentaire ! } ... } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 29 Test unitaire avec Junit ● Evidence : il faut tester ses programmes ● Constat : manque de méthodologie ● – Faire un main dans une classe séparée – tester « un peu au pif » Solution : Junit (sur http://www.junit.org) – Pour chaque classe MaClasse, créer une classe MaClasseTest qui hérite de junit.framework.TestCase – Pour chaque méthode maMethode() de MaClasse (modulo surcharge), créer (au moins) une méthode de test testMaMethode() dans MaClasseTest – Il faut aussi créer des test unitaires pour tester la combinaison de plusieurs méthodes Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 30 Structure d'une classe de test import junit.framework.*; public class MaClasseTest extends TestCase { private MaClasse mon_objet; // pour tout les tests public MaClasseTest() { mon_objet = null; } public void setUp() { // préparation global de tous les tests mon_objet = new MaClasse(...); } public void setDown() { // terminaison de tous les tests mon_objet = null; } public void testMaMethode() { // test unitaire // ici les tests pour maMethode() dans MaClasse // on peut utiliser mon_objet ou utiliser des // objets MaClasse locaux } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 31 Contenu d'un test unitaire Chaque test contient: ● Du code java réalisant les tests ● Des assertions de tests vérifiant les résultats/modifications assertTrue(<expression booléenne>) => vrai ok, faux erreur de test Remarque: assertTrue(true) toujours vrai et assertTrue(false) toujours faux ● import junit.framework.*; public class MaClasseTest extends TestCase { ... public void testMaMethode() { // test unitaire // ici les tests pour maMethode() dans MaClasse // on peut utiliser mon_objet ou utiliser des // objets MaClasse locaux <resultat> = mon_objet.MaMethode(...); assertTrue(<test booléen sur le résultat>); // ... autres tests possibles } } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 32 Exemple : Test de la cuve bornée 1) Tester les prérequis public class CuveBorneeTest extends TestCase { private CuveBornee cuve1; ... public void setUp() { cuve1 = new CuveBornee(10.0); } ... public void testRemplir() { // 1) Tester le prérequis // 1.a) prérequis non valide try { cuve1.remplir(12.0); assertTrue(false); // il ne faut pas arriver ici } catch(CuvePleineException e) { assertTrue(true); // il faut arriver ici } // 1.b) prérequis valide try { cuve1.remplir(4.0); assertTrue(true); // il faut arriver ici } catch(CuvePleineException e) { assertTrue(false); // il ne faut pas arriver ici } } ... } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 33 Exemple : Test de la cuve bornée 2) Tester les garanties public class CuveBorneeTest extends TestCase { ... public void testRemplir() { // 1) Tester le prérequis ... double old_niveau = cuve1.getNiveau(); // avant de remplir try { cuve1.remplir(4.0); assertTrue(true); // il faut arriver ici } catch(CuvePleineException e) { assertTrue(false); // il ne faut pas arriver ici } // 2) Tester les garanties // GARANTIE: this.getNiveau() = old.getNiveau()+liquide assertTrue(cuve1.getNiveau()=old_niveau+4.0); } ... } Remarque : on pourrait aussi cloner la cuve avant de la modifier Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 34 Exemple : Test de la cuve bornée 3) Lancer les tests java junit.swingui.TestRunner CuveBorneeTest Attention : il faut que junit.jar et CuveBorneeTest soient dans le CLASSPATH Remarque: en TME, nous utiliserons BlueJ, un ide qui intègre junit cf. http://www.bluej.org Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 35 Erreurs logiques ● ● Dans la conception par contrat, on a vu comment: – Décrire au sein du fournisseur la vérification que les clients respectent leurs prérequis – Décrire dans un test unitaire la vérification que le fournisseur fournit ses garanties du point de vue du client – Cela nous aide à écrire proprement les intéractions entre clients et fournisseurs mais cela ne nous aide pas à tester la logique interne du fournisseur Les erreurs logiques sont des bugs – Même en étant très rigoureux (par exemple en adoptant la conception par contrat) et en écrivant beaucoup de tests unitaires, il est très difficile de s'assurer que le code d'une méthode fait bien son travail : point de vue du fournisseur => on veut aussi tester dans le code du fournisseur Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 36 Les assertions ● Java fournit un mécanisme : les assertions – ● assert(<condition booléenne>) ● Si vrai : le programme se poursuit normalement ● Si faux : une exception AssertionError est levée Attention 1: ne pas confondre – assertTrue() est une assertion de test pour les tests unitaires, c'est un mécanisme spécifique de junit – assert() est une assertion logique pour le code du fournisseur, c'est un mécanisme standard de Java (> 1.4) ● Attention 2 : les assertions logiques ne font pas partie du code fonctionnel, on peut les désactiver avec: java -da MaClasseMain Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 37 Exemple : assertions pour la cuve bornée En général, on sépare: ● Préconditions : assertions avant un traitement ● Postconditions : assertions après un traitement public class CuveBornee { ... public void remplir(double liquide) throws CuvePleineException { if(niveau+liquide>limite) throw new CuvePleinException(); // PRECONDITION: le niveau est correct assert(niveau>=0 && niveau<=limite); niveau = niveau+liquide; // POSTCONDITION: le niveau reste correct assert(niveau>=0 && niveau<=limite); } ... } Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 38 Conclusion ● A partir de maintenant, vous savez: – Comment traiter les exceptions: – Traitement immédiat ● Délégation ou filtrage Comment concevoir des classes robustes ● ● Vision contrat client/fournisseur – Votre rôle est de définir le fournisseur Prérequis et exceptions personnalisées ● Garanties et tests unitaires Comment ajouter des assertions pour débusquer les bugs ● – => A partir de maintenant, vos programmes Java doivent être robustes Programmation Objet en Java – Cours 5 Exception – (C) 2005, Frédéric Peschanski 39 Le mot de la fin ● Exercice à la maison: – ● ● Compléter la classe de cuve bornée Pointeurs: – Junit : http://www.junit.org – Conception par contrat : http://www.eiffel.com – Assertions logiques : http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html La semaine prochaine: – Les design patterns (1/2)