java - Laurent Henocque
Transcription
java - Laurent Henocque
Cours POO Java Séances 4, 5, 6 Laurent Henocque http://laurent.henocque.free.fr/ Enseignant Chercheur ESIL/INFO France http://laurent.henocque.perso.esil.univmed.fr/ mis à jour en Octobre 2011 Licence Creative Commons Cette création est mise à disposition selon le Contrat Paternité-Partage des Conditions Initiales à l'Identique 2.0 France disponible en ligne http://creativecommons.org/licenses/by-sa/2.0/fr/ ou par courrier postal à Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. Avertissement • Ce support est la deuxième partie d’un cours. Il complète une présentation de Java donnée dans le cours d’Alain Samuel • Présente des caractéristiques avancées et spécifiques de Java • Rappel: toute l’information sur Java existe dans la doc en ligne grâce à Javadoc : les commentaires des sources deviennent de la documentation Généralités Java est Interprété • Le code source est traduit par ‘javac’ en byte-code indépendant de la machine • Le byte code est exécuté (émulé) par ‘java’ (.class) ou ‘javaw’ (.jar) • L’architecture est neutre: il suffit d'une machine virtuelle Java sur la machine d’exécution • Apparence des applications préservée sur toutes les plate formes. • Note: c’est un concept déjà utilisé pour le langage COBOL dans le passé Java est robuste • Plus simple que C++, mais comme lui un langage à typage statique fort (détection à la compilation) • Gestion très différente de la mémoire (pointeurs, libération) • Nombreux contrôles de débordement, utilisation systématique des exceptions dans les bibliothèques d'outils Java est Sécurisé • Verifier : vérifie le byte code. (Chaque fichier ".class" possède une marque calculée, permettant d'interdire les modifications triviales : insertion de virus par simple ajout par exemple) • Class Loader : responsable du chargement des classes. • Security Manager : accès aux ressources Java est un langage « hautes » performances • En fonctionnalités, mais pas en vitesse d’exécution (pénalisée par la JVM) • Performances restaurées par la compilation "Just In Time", qui permet à la machine virtuelle de mémoriser, puis ré-exécuter le code natif correspondant à une portion de boucle • « multithreaded » (éventuellement sur une machine mono-processeur) Performance • Les JVM sont plus lentes que du code natif mais: – compilation "just in time": une boucle déroulée 10000 fois n'est interprétée qu'une seule fois • Java est gourmand en mémoire mais – les options de lancement "java -X..." permettent de contrôler les tailles des différentes piles et tas (max heap, start heap, stack size): – java -Xmx32m -Xms16m -Xss16m Java est Moderne • à typage statique, mais ‘dynamique’ par essence • adapté à un environnement évolutif • adapté aux plateformes mobiles • interprété donc portable • lié aux design patterns par essence: • itérateurs, objets fonction, inversion de contrôle etc... De C++ à Java en quelques différences • Pas de structures ni d’unions -> classes • Pas d'alias de types (typedef) • Pas de pré-processeur (mais on pourrait l'utiliser) • Pas de variables ni de fonctions en dehors des classes (globales) • Pas d'héritage multiple de classes • Pas de surcharge d’opérateur • Pas de passage par copie pour les objets • Pas d'appel de "delete" De C++ à Java en quelques différences • Les varargs sont mieux faits • Les templates s'appellent generics, et sont différents • Java propose une syntaxe avancée pour la boucle for • Java permet la conversion automatique des types de base vers des objets (wrapping ou boxing/unboxing) • Les enums sont typés • Java permet d'attacher au code des annotations Le mot clé ‘Final’ Le mot Clé ‘Final’ peut être utilisé pour : • une classe: celle ci ne peut être sous-classée • une méthode: celli ce ne peut être redéfinie par une sous classe • un attribut ou paramètre : celui ci ne peut plus changer de valeur une fois initialisé Classe ‘Final’ • Une classe peut être déclarée ‘final’ pour des raisons de sécurité (un hacker pourrait substituer une instance d’une sous classe). C’est le cas de ‘String’. • Ou pour des raisons de design, par exemple parce qu’elle implante un algorithme sophistiqué ne pouvant simplement être adapté par héritage (car non conçu pour) Méthode ‘Final’ Une méthode peut être déclarée ‘final’ pour les mêmes raisons qu’une classe, sans toutefois interdire la totalité de la classe: • sécurité • design Paramètre ‘Final’ Un paramètre est déclaré ‘final’ pour indiquer au compilateur et au lecteur que sa valeur ne sera jamais modifiée • Améliore la compréhension et la maintenance des algorithmes • Permet des gains de performance par une compilation adaptée (intérêt mineur) Non utilisé si tout est ‘final’ Objet ‘Final’ • Lorsqu’un pointeur vers un objet est déclaré ‘final’, cela signifie que l’identité de l’objet ne sera jamais modifiée. • Toutefois, son état peut changer bien entendu. • (en fait, le ‘pointeur’ est ‘final’) Exceptions en Java Principes Fondateurs • Le programme qui détecte une erreur ne sait pas comment cette erreur pourra être traitée dans le programme appelant (commentaire français/anglais/ ignoré/...) • Si une erreur interdit de poursuivre l’exécution d’un programme, il est inutile d’obliger le programmeur à prévoir sa terminaison harmonieuse Mécanisme • Les exceptions fournissent un flux de contrôle distinct de celui offert par la terminaison des fonctions • C’est en quelque sorte un ‘abort’ de l’exécution en cours, qui peut être intercepté plus haut dans l’arbre d’appel • Le programme qui lance l’exception ignore ce qu’en fait le programme appelant Retour Sans Terminaison des Fonctions ... Type des Exceptions Java • Les exceptions dérivent de la classe Exception, plus rarement des classes Throwable ou Error. • Elles implémentent un certain nombre de fonctions utilitaires, pour les initialiser ou les utiliser pour transmettre des informations au programme appelant Hiérarchie des Exceptions Throwable Error AssertionError Exception RuntimeException Vos exceptions ... API de Throwable • fillInStackTrace, • getLocalizedMessage, • getMessage, • getStackTrace, • initCause, getCause, • setStackTrace, printStackTrace Throws : déclaration dans les méthodes • Une méthode doit spécifier ses exceptions dans son entête • public void ma_methode (….) throws my_exception1, IOException { … } • Ce sont les exceptions qu’elle peut créer ET les exceptions pouvant être lancées par des méthodes qu’elle appelle et qu’elle n’intercepte pas (par un catch) Cas particulier de RuntimeException • Toute fonction susceptible d’être traversée par une exception doit le spécifier dans sa clause “throws” • Ce n’est pas le cas pour les exceptions sous classes de RuntimeException • A n’utiliser que si une exception n’est pas susceptible d’être interceptée par le client, et si ce n’est pas une assertion. Try-Catch : capture des exceptions • Après un bloc try, passage dans le premier catch de type compatible • L’ordre des catch permet de définir des préférences … try { … } catch (type-exception1 v1) { … } catch (type-exception2 v2) { … } … finally { // on passe nécessairement par ici ... } Lancement d'exception • Une exception doit toujours être allouée par new et lancée avec "throw" • On peut lancer une exception dans un catch void foo() throws MonException { ... if(...)throw new MonException(...); ... } catch (...) universel ? • En Java, toutes les exceptions dérivent de Exception, et tout ce qui peut être "lancé" dérive de Throwable (Error + Exception) • catch(Exception e){} intercepte donc toutes les exceptions • Noter que l'on n'est pas supposé écrire catch (Throwable t){}, car on laisse normalement la machine virtuelle gérer les erreurs (AssertionError, ou échec de lancement de Thread par exemple) Importance de ‘Finally’ • La clause finally est toujours exécutée, qu’il y ait eu ou non exception • C’est donc l’endroit requis pour placer du code de nettoyage. • C’est d’ailleurs de toutes façons le meilleur endroit pour le faire, qu’il y ait ou non des exceptions : protège aussi contre des ‘return’, ‘break’, ‘continue’ Le ‘bloc finally’ { ... } devient try { ... } finally { ... } ou {try { ... } finally { ... }} Exceptions Prédéfinies • Le noyau et les bibliothèques Java utilisent intensivement les exceptions • ArrayIndexOutOfBoundsException • BadStringOperationException • IOException • NoSuchMethodException • ... Assertions Assert, et les Invariants • La programmation, et la conception par contrat, reposent sur l’utilisation d’invariants. • Un invariant est une propriété logique toujours vraie en un point donné de l’exécution d’un programme • Un invariant n’est pas “testé” par un programme, il est “garanti” par “assert” utilisation de "assert" • L'instruction "assert" permet la spécification contrôlée d'invariants • On ne doit pas utiliser de tests (if ...then...else) pour contrôler les invariants mais toujours utiliser "assert" • Cela sépare deux notions distinctes, et améliore la lisibilité des programmes. "assert" permet que la spécification d'un programme soit testée à l'exécution. Ou? Quand? • Les invariants sont décrits par le programmeur avant même de programmer • Ils restent dans le code pendant toute sa durée de vie • Ils sont testés de manière invisible pendant le développement (“debug”) • Ils sont ignorés en production assert en Java • Pour évaluer les assertions, il faut exécuter avec l'option "-ea" ("enable assertions") • java -ea ... • Pour lancer une assertion, • assert expr_1 [: expr_str] • "assert" lance un “AssertionError” si son expr_1 est fausse, et la trace appelle la méthode "toString" de extr_str. Niveau de contrôle • Le test d'assertions peut être contrôlé à trois niveaux : global, package et classe. Ces fonctions sont dans l’API de ClassLoader: • public void setDefaultAssertionStatus(boolean enabled); • public synchronized void setPackageAssertionStatus(String packageName, boolean enabled); • public void setClassAssertionStatus(String className, boolean enabled); • public void clearAssertionStatus(); • public boolean desiredAssertionStatus(); Types d’Invariants • Invariants de classe • Pré conditions • Post conditions • Invariants d’étape d’algorithme • Variants de boucle • Invariants de configuration, d’architecture etc... Invariants de classe • Un invariant de classe décrit les propriétés logiques qui sont vraies d’un type donné: • après l’exécution de tout constructeur • pendant et après l’exécution de tout accesseur • au début et après l’exécution de tout modifieur Invariants de Classe Class Stack { int arraySize; int top; private void invariant(){ assert top>=0; assert top<=size; } void push(int i){ invariant(); ... // le code invariant(); } } Tester les invariants sans toucher au code • Les fonctions d’accès à l’état d’une classe (accesseurs), comme les modifieurs, peuvent utiliser de multiples instructions return. • Tester l’invariant de classe comme les postconditions en fin d’appel s’avère impossible si l’on doit intervenir en de multiples endroits du code. De plus ces tests doivent être réalisés après l’évaluation de toute valeur de retour • On peut utiliser la construction try {code} finally{...}, qui permet de ne pas intervenir du tout sur le code original. Invariants de Classe, avec Finally Class Stack { ... boolean invariant(){...} void push(int i){invariant(); try{ ... // le code }finally{invariant();} int pop(){invariant(); try{ ... // le code return la_valeur; }finally{invariant();} //appelé!!! } Interface “Checked" • On peut Imposer la définition d’une fonction de test des invariants de classe. public interface Checked { public void invariant(); } public class MaClasse implements Checked{ public void invariant(){ ... return; } Interdire la Récursion d’appel d’invariant private int lock=0; // invariant de classe private void invariant(){try{ assert lock++ == 0; assert size==array.length; assert foo(); // récursion assert pos<=size; assert pos>=0; }finally{lock=0;}} private boolean foo(){invariant(); ... return ...;} Permettre la Récursion d’invariant private int lock=0; // invariant de classe private void invariant(){try{ if (lock++ != 0) return true; assert size==array.length; assert(foo()); // récursion assert pos<=size; assert pos>=0; }finally{--lock;}} private boolean foo(){invariant(); ... return ...;} Préconditions Class Stack { int arraySize; int top; boolean invariant(){...} void getValueAtPos(int i){ invariant(); assert i>=0; //séparés exprès assert i<top; //séparés exprès ... // le code } } Post conditions • Une post condition doit être testée à la fin de l’exécution d’une fonction. Le meilleur endroit est dans le bloc finally (évite de traiter tous les return) void foo(int i){try{invariant(); ... // le code }finally{ assert post_condition; assert invariant(); } } Delta Conditions • Une delta condition est une post condition qui vérifie une propriété logique à la fin d’un traitement faisant intervenir l’état du système au début de l’exécution. int depiler(){invariant(); final int auxpos=pos; // pour la D condition try{ assert (!estVide()); return array[--pos]; } finally { assert auxpos==pos+1; // évalue la D condition! invariant();} } Invariants d’étape d’Algorithme void f(int i){ //on cherche dans une liste //une donnée qui s’y trouve Link p=...; while (p!=null && p.data!=i)){ p=p.next; } assert p!=null; //bingo p.doSomething();//on peut le faire } Variants de Boucle int lastLoopVar=10000; int loopVar=20; while (1){ assert lastLoopVar>loopVar; if(loopVar<=0)break; // (garantit la terminaison) ... lastLoopVar=loopVar--; }! Une difficulté née de “try / Finally” • Lorsqu’un assert ou une exception est lancé pendant l’exécution du corps d’une fonction, la clause “finally” est appelée. • Si celle ci lance également un assert, ce dernier masquera le précédent, ce qui a pour inconvénient de cacher le point d’origine de l’arrêt du programme. try / finally • Un assert ou une exception dans le finally cachera le Throwable en cours. Pour le révéler, il faut intercepter les ‘Error’ et les ‘RuntimeException’ // pre conditions ici try{ /* code , y compris conditions d’algorithmes*/ } catch (RuntimeException e){e.printStackTrace(); throw e; } catch (Error e){e.printStackTrace(); throw e; }finally{ //post conditions ici, généralement lancées si le code //a été interrompu par un Throwable invariant(); } Quelques Avantages de l’Utilisation d’Assert • Les invariants constituent une documentation toujours exacte • On sépare le contenu algorithmique de la vérification de ses conditions d’utilisation • On se prémunit d’avance contre les bugs de régression, lors de modifications d’une classe • On détecte les conditions logiques des erreurs au plus tôt, alors que leurs conséquences peuvent se manifester en des lieux inopportuns. Conception dirigée par les tests et invariants 1) identification des classes 2) écriture de tests automatiques (avec assert) 3) définition des structures de données 4) écriture des invariants, assertions 5) codage 6) exécution TDD et ADD • Test Driven Development (ou Design) et Assertion Driven Design sont des acronymes utilisés dans le développement itératif et rapide • Encouragent concision, maintenabilité du code et couverture des tests. • Réduisent massivement les recours au débugger • Augmentent la productivité • ‘Test first’, ‘Extreme Programming’, ... Automatiser les tests en utilisant Assert Cela nécessite: • de vérifier automatiquement que assert est activé • de comparer avec assert des valeurs attendues avec les valeurs observées. Notamment le produit de la fonction toString. • d’être en mesure de tester qu’un assert déclenche effectivement, sans toutefois arrêter le programme Vérifier que les Assertions sont testées } public static void main(String[]args){ try { // vérification du test d’assertions assert false; throw new RuntimeException("JVM option '-ea'");} catch (AssertionError e){} ... ... ... // ici les tests ... ... System.out.println("Test unitaire validé"); Automatiser les tests par des asserts { ... Stack p=new Stack(2); //System.out.println(p); assert p.isEmpty(); assert "()".equals(p.toString()); p.push (1); //System.out.println(p); p.push (2); //System.out.println(p); assert "(1,2)".equals(p.toString()); ... } Tester qu’un Assert déclenche ... Stack p=new Stack(2); try { p.pop(); throw new RuntimeException(""); } catch (AssertionError e){ assert "cannot pop: empty".equals(e.getMessage()); } ... • Dépiler une pile vide doit déclencher l’assert ayant "cannot pop: empty" comme message. • La RuntimeException ne devrait pas être atteinte • ATTENTION: on veut normalement désigner le premier assert déclenché - même s’il y a eu cascade Tester qu’une Exception est lancée ... Stack p=new Stack(2); try { p.pop(); assert false: «must not happen»; } catch (MyEmptyStackException e){ assert "cannot pop: empty".equals(e.getMessage()); } ... • Dépiler une pile vide doit lancer une exception de type MyEmptyStackException et ayant "cannot pop: empty" comme message. • Le ‘assert false;’ ne doit pas être atteint try /finally et performance (1/2) Pas de différence perceptible de temps d’exécution pour les deux fonctions ci dessous long testPerf(long j){ long i=j; i=i+1; return i; } long testFinally(long j){try{ long i=j; i=i+2; return i; }finally{ assert j>=0; } } try /finally et performance (2/2) long ret=0; // première boucle pour initialiser la jvm for (int k=0;k<10;k++) for (long i=0;i<k*10000;i++) ret+=p.testPerf(i+k); // le test maintenant long t0=new Date().getTime(); for (int k=0;k<100;k++) for (long i=0;i<k*1000000;i++) ret+=p.testPerf(i+k); long t1=new Date().getTime(); for (int k=0;k<100;k++) for (long i=0;i<k*1000000;i++) ret+= testFinally(i+k); long t2=new Date().getTime(); System.out.println("test perf en : " + (t1-t0) + " millis"); System.out.println("test perf 2 en : " + (t2-t1) + " millis"); >>Test de performance brute de la section finally >>test perf en : 5953 millis >>test perf 2 en : 5895 millis JUnit • Le projet JUnit offre des services sophistiqués de tests unitaires • JUnit permet de déclarer des fonctions de test avec des annotations, vues plus loin • JUnit déclare des fonctions comme assertEquals(...) etc... • L’utilisation de JUnit est complémentaire de ce qui vient d’être vu. Introspection Les classes • La classe est l’élément de base de Java. Tout est classe sauf les types primitifs (int, float, double, ...) et les tableaux • Il n’y a pas de fonctions, seulement des méthodes • Toutes les classes dérivent d'une superclasse commune java.lang.Object • “Object” fournit un grand nombre de services La classe Object API fondamentale • protected Object clone() – • boolean equals(Object obj) – • renvoie la classe d'un objet int hashCode() ! – • appelé par le garbage collector avant la récupération de l'espace occupé par l'objet. Class getClass() – • permet de comparer deux objets par le contenu (requis pour les tables de hash code). protected void finalize() – • crée et retourne une copie de l'objet. Remplace l’opérateur d’affectation copie (=) renvoie la clé de hash code associée à l'objet String toString() – retourne une chaîne de caractère. Utilisée par la jvm pour toutes les conversions en chaînes La classe Object API - Threads • void notify() " – réveille un thread bloqué sur cet objet. • void notifyAll() – réveille tous les threads en attente pour cet objet. • void wait() – bloque un thread sur cet objet, en attendant qu'un autre appelle notify() La classe "Class" public final class Class extends Object implements Serializable • Les classes sont construites par la JVM automatiquement, et sur appel de defineClass dans un class loader. Afficher un nom de classe void printClassName(Object o) { System.out.println("La classe de " + o + " est " + o.getClass().getName()); } • La formulation suivante s'applique aussi à un type de base (int, float etc...): System.out.println("Le nom de classe de o est : "+ o.class.getName()); Class et Génération de code à la volée • Un programme Java peut • générer le source d'une classe, • invoquer le compilateur sur ce source, • charger dynamiquement la classe avec la fonction "forName" • pour enfin en créer des instances et utiliser leur API • (Ce n'est possible en C++ qu'au prix de grandes difficultés) Class et Paramétrage • Charger dynamiquement une classe en Java rend possible tout J2EE • Un fichier de configuration (xml) fournit l nom d’une classe inconnue du framework, le framework peut en créer des instances, et utiliser leur API • Difficile à réaliser en C++ Méthodes statiques de Class static Class forName(String className) – renvoie l'objet Class d'un nom donné – Class t = Class.forName ("java.lang.Thread") static Class forName(String name, boolean initialize, ClassLoader loader) – pareil, mais en utilisant un class loader particulier, avec l'option d'initialiser ou non la classe désignée API de Class Nom, Héritage • String getName() – le nom • Package getPackage() – le package • int getModifiers() – un int contenant des booléens pour public, static etc... • Class getSuperclass() – la superclasse • Class[] getInterfaces() – retourne les interfaces implantées par la classe API de Class Classes Imbriquées • Class[] getClasses() – tableau de toutes les classes et interfaces publiques membres de cette classe • Class[] getDeclaredClasses() – tableau de toutes les classes et interfaces membres déclarées par la classe • Class getDeclaringClass() – retourne la classe dont on est membre (éventuellement) API de Class Informations de Type • boolean isAssignableFrom(Class cls) – vrai si cls est une sous classe ou une classe qui implémente notre interface • boolean isInstance(Object obj) – est ce que obj est une instance de cette classe • boolean isInterface() – interface?. • boolean isPrimitive() – type primitif? (int, void, etc...) • boolean isArray() – un tableau? • Class getComponentType() – le type des composants d'un "array". API de Class Accès aux constructeurs • Constructor getConstructor(Class[] parameterTypes) • Constructor[] getConstructors() • Constructor getDeclaredConstructor(Class[] parameterTypes) • Constructor[] getDeclaredConstructors() – retournent les constructeurs publics, ou déclarés • Object newInstance() – crée une nouvelle instance avec le constructeur sans arguments Accès aux membres • Field getDeclaredField(String name) • Field[] getDeclaredFields() • Field getField(String name) • Field[] getFields() – retournent les champs de la classe publics ou non • Method getDeclaredMethod(String name, Class[] parameterTypes) • Method[] getDeclaredMethods() • Method getMethod(String name, Class[] parameterTypes) • Method[] getMethods() – retournent les méthodes Sécurité, ressources • ClassLoader getClassLoader() – le chargeur de cette classe. • ProtectionDomain getProtectionDomain() – les permissions accordées à cette classe • URL getResource(String name) – trouve une ressource de nom donné (les ressources sont tous les fichiers de paramètres, ou de données accessibles dans le classPath - ou un jar). • InputStream getResourceAsStream(String name) – renvoie directement un flux d'entrée sur la ressource, prêt à en lire le contenu. • Object[] getSigners() – renvoie les "signers" de la classe - certificats d'authenticité-. • boolean desiredAssertionStatus() – renvoie le statut par défaut pour assert de la classe (les assertions peuvent être contrôlées sur une base "classe par classe") Contructeurs, champs, méthodes • Les classes Constructor, Field et Method offrent une API permettant d’analyser un constructeur, un champ ou une méthode. • Types des paramètres d’une fonction • Type de la valeur de retour • Type d’un attribut • Appel de la méthode ou avec des paramètres appropriés • Création d’une instance avec des paramètres appropriés Pointeurs de Fonctions Et classes anonymes Pointeurs de fonctions • Java ne permet pas de manipuler explicitement de pointeur de fonction. • Le seul moyen d’utiliser ces pointeurs se fait via l’appel de méthodes, qui sont toujours virtuelles en Java. public interface Demon { public abstract void execute(/*params*/); } Pointeurs de fonctions class MonDemon implements Demon{... } class MaClasse { Demon undemon; MaClasse(Demon d ){undemon=d;} void executeDemon(){ undemon.execute(/*params*/);} } Classes anonymes Java permet de déclarer des classes "au vol", anonymes. abstract class A { abstract void g(); } class Z { void h(A a){ ... a.g(); ...} } ... { Z z=new Z(); z.h(new A(){ void g(){ ... }}); } Threads Section Minimale d’introduction aux API des Threads en vue du cours d’IHM Les threads • Petits morceaux de programme « légers » faits pour fonctionner « en parallèle » en partageant les données du même processus • écriture d’un texte • bande son • animation d’une image Etats des threads • Un thread peut être dans quatre états distincts • créé • actif • endormi • tué Option 1: Etendre Thread class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime;} public void run() {/*calcule les nombres premiers supérieurs à minPrime*/} } //////// pour lancer: PrimeThread p = new PrimeThread(143); p.start(); Option 2: Implanter Runnable class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) {this.minPrime = minPrime;} public void run() {/*calcule les nombres premiers supérieurs à minPrime*/ } } //////// pour lancer: PrimeRun p = new PrimeRun(143); new Thread(p).start(); API de contrôle de thread void start() lance le thread. la machine virtuelle java appelle run() void run() void interrupt(); void destroy() void setDaemon(boolean on) void join([long millis [, int nanos]]) bloque le thread courant jusqu’à ce que le thread support de la méthode ait terminé son activité, avec un timeout pour s’échapper Démons • Lorsque les seuls threads restant en cours d’exécution sont des démons, la jvm s’arrête. • Un thread démon est par exemple une boucle infinie d’accès à des ressources (le thread qui récupère les fichiers image à partir des uris dans un navigateur par exemple) API Statique de thread static Thread currentThread() //le thread courant static int activeCount() //threads actifs dans le groupe static boolean holdsLock(Object obj) // le thread courant a t'il un lock sur l'objet? static boolean interrupted() static void sleep(long millis[, int nanos]) static void yield() //arrête temporairement static int enumerate(Thread[] tarray) du groupe static void dumpStack() //les threads actifs API de thread: Accesseurs ClassLoader getContextClassLoader() String getName(); void setName(String name) int getPriority(); void setPriority(int newPriority); ThreadGroup getThreadGroup(); boolean isAlive(); boolean isDaemon() ; boolean isInterrupted() void setContextClassLoader(ClassLoader cl) void checkAccess() throws SecurityException possibilité de modifier ce thread? via security manager Classes Génériques Les classes Génériques • Java fournit un mécanisme permettant de paramétrer des classes, fonctions et attributs par des types • Ces constructions sont très utiles pour permettre un contrôle à la compilation (typage fort) des données passées à des programmes • Cela permet par exemple de créer des conteneurs “homogènes” Classes Génériques http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf • Un programme sans génériques • Un programme avec génériques Déclaration d'interfaces génériques Sémantique des génériques • Très différente de C++ qui génère une nouvelle classe pour chaque spécialisation d'un template • Ici, le compilateur génère une seule représentation de la classe générique, une fois pour toutes. L'information de type explicite ou implicite (passage d'un paramètre de type connu) lui permet de générer automatiquement les casts. • Le code Java produit par des génériques reste compatible avec du code java standard Génériques et Sous typage • Le comportement du compilateur pour le sous typage peut paraître contre intuitif: List<String> ls = new ArrayList<String>(); //ok List<Object> lo = ls; //invalide • C'est normal si l'on considère cet exemple: lo.add(new Object()); // ok String s = ls.get(0); // invalide: on a un "Object" à la place d'une "String" Le type "Wildcard" • Comment faire alors pour déclarer une fonction qui prendrait en paramètre un conteneur de n'importe quoi? • Mauvaise idée: Le type "Wildcard" • La solution: utiliser le "Wildcard" Désigner une sous hiérarchie • Considérons une méthode devant dessiner une liste de formes (naïvement List<Shape>). • Le compilateur refusera un appel avec List<Circle>, alors que c'est raisonnable Désigner une sous hiérarchie • Il faut utiliser: public void drawAll( List<? extends Shape> shapes) { ... } Méthodes Génériques • On peut également paramétrer des méthodes par un type. • On utilise ce mécanisme normalement quand il existe une dépendance – entre les types d'un ou plusieurs paramètres – et/ou celui de la valeur de retour. Méthodes Génériques • Exemple: ranger les éléments d'un tableau dans une collection. Ne compile pas: • Il faut écrire: Wildcard OU Générique ? • En cas d'ambiguïté, s'il n'y a pas de dépendance entre types, on préfère le "Wildcard" • VS: Wildcard ET générique? • Voici la déclaration de la fonction copy des collections. • Elle copie une liste de T ou de sous types (src) vers une liste de T Génériques imbriqués • Bien sûr, on peut utiliser un type générique comme paramètre d'un autre. • Exemple: la fonction "drawAll" conserve un historique dans une liste statique: Super • <? extends T> signifie désigne un sous type de T • <? super T> désigne un supertype de T, s’il est utilisé pour l’initialisation du générique. • List<? super String> st; • st=new ArrayList<String>(); //ok • st=new ArrayList<Object>(); //ok • st=new ArrayList<Serializable> (); // String implémente Serializable Implémentation d’interfaces multiples • <? extends A & B & C> Pour comprendre les Génériques • Tout repose sur ce que le compilateur connaît. • List<? super String> st; dit que tout sous type de String sera compatible pour l’insertion. Pas plus. • Une fois que le code compile, les paramètres génériques sont oubliés (et inaccessibles - pas d’introspection) Présentation de l’API Math • C’est la bibliothèque mathématique, elle définit deux constantes E et PI (représentés sous forme de double) • Toutes ses méthodes sont statiques et retournent des ‘double’ (sauf rint et round): • sin, cos, tan, asin, acos, atan, exp et log, sqrt et cbrt, élévation à une puissance pow, abs • approximations numériques entières : ceil, floor, rint, round • nombre aléatoire random(dans [0, 1]) • fonctions de comparaison min et max • fonctions hyperboliques sinh, cosh et tanh • ... StrictMath, BigInteger • La classe StrictMath garantit les résultats de certains algorithmes numériques de références mais dans certains cas cela peut conduire à une perte d’efficacité • java.math offre deux classes • BigInteger et • BigDecimal • permettant de manipuler des nombres avec une grande précision … La classe File • Gestion et stockage des fichiers (création, suppression, position, etc.) par le biais d’une représentation abstraite des fichiers et répertoires • File(String) crée la représentation abstraite d’un fichier ou répertoire • isFile() • isDirectory() • listFiles() Collections • java.util fournit des classes de collections de données • Ces classes fournissent des algorithmes nombreux et efficaces • Elles respectent les concepts sous-jacents aux catégories auxquelles elles appartiennent (accès lecture en O(1) ou O (n) par exemple) Avantages des Collections • Réduction des efforts de programmation grâce à l’apport de structures de données et d’algorithmes riches et souples • Fiabilité et performances des programmes grâce à la qualité des implémentations fournies pour ces structures de données et ces algorithmes • Amélioration de la réutilisation des programmes qui s’appuient sur ces collections standards Catégories de Collections Il existe trois grandes catégorie de classes de ce type : • les séquences (chaque élément à un rang) • les ensembles (pas de répétition, pas de rang) • les tables associatives (couples <clé, valeur>) Séquences AbstractList<E> AbstractSequentialList<E> Vector<E> ArrayList<E> LinkedList<E> Stack<E> ArrayList est rapide en accès aléatoire, mauvais en insertion LinkedList est mauvais en accès aléatoire, rapide en insertion Vector offre des services complémentaires (redimensionnement) Ensembles AbstractSet<E> HashSet<E> TreeSet<E> LinkedHashSet<E> HashSet ne garantit pas l’ordre des éléments LinkedHashSet utilise une liste chainée supplémentaire et préserve l’ordre d’insertion TreeSet est un arbre binaire de recherche Tables Associatives AbstractMap<E> HashMap<E> TreeMap<E> WeakHashMapMap<E> LinkedHashMap<E> IdentityHashMap<E> IdentityHashmap compare des références (==) WeakHashMap utilise des références weak: pas de fuites mémoire Comparator<T> • Une classe définissant un ordre total • Les ‘Comparator’sont arguments des méthodes de tri: Collections.sort ou Arrays.sort • Les comparateurs sont aussi utilisés pour les collections ordonnées ‘sorted sets’ ou ‘sorted maps’ ou pour donner un ordre à des objets qui en sont naturellement dépourvus. Cohérence avec l’égalité • Une fonction de comparaison doit être cohérente avec l’égalité: c.compare(e1, e2)==0 • si et seulement si e1.equals(e2) API Commune aux Séquences et Ensembles • boolean add(E e) • boolean remove(E e) • boolean contains(Object o) • boolean isEmpty() • int size() Api des Séquences • E get (int i) • E set (int i, E e) • void add (int i, E e) • E remove (int i) Classe Stack<E> • E push(E e) // empile e en sommet de pile • E pop() // dépile le sommet de pile • E peek() // retourne le sommet de pile API des Ensembles • Les méthodes générales s’appliquent aux ensembles tout en garantissant l’unicité des éléments. La classe TreeSet<E> propose en plus: • SortedSet<E> headSet(E e) // éléments < e • SortedSet<E> tailSet(E e) // éléments >= à e • SortedSet<E> subSet(E e1, E e2) // éléments compris entre e1(inclus) et e2(exclus) API des Hash Tables • V get(Object k) • V put(K k, V v) // associe v à la clé k et renvoie l’ancienne valeur (ou null) • V remove(Object k) // supprime la clé k et renvoie l’ancienne valeur (ou null) • boolean containsKey(Object k) • Set<K> keySet() // l’ensemble des clés • Collection<V> values() // renvoie la collection des valeurs La classe Collections • Offre des algorithmes sous forme de méthodes statiques • void reverse(List<?> l) // inverse l • void shuffle(List<?> l) // permute aléatoirement l • <T> int binarySearch(List<? extends Comparable<? super T>> l, T k) • (cherche k dans la séquence triée l en utilisant un algorithme dichotomique, renvoie la position de k dans la séquence Autres Fonctions de Collections • void swap(List<?> l, int i, int j) // échange les valeurs qui se trouvent en positions i et j dans l • <T extends Object & Comparable<? super T>> T max (Collection<? extends T> c) // max de la collection c (idem pour min) selon l’ordre naturel • <T> boolean replaceAll (List<T> l, T v1, T v2) // remplace tous les éléments v1 par v2 dans la séquence l • <T extends Comparable<? super T>> void sort(List<T> l) // trie la séquence en utilisant un algorithme de tri par fusion Itérateurs Itérateurs • Un Iterator<E> permet de parcourir une collection (séquence ou ensemble) • Il se positionne sur le premier élément de la collection et en donne successivement chaque élément: ArrayList<E>, LinkedList<E>, TreeSet<E>, HashSet<E>, etc. • Pour les ensembles, l’ordre d’itération est arbitraire. • Les tables associatives ne disposent pas d’itérateurs. Pour parcourir la collection des clés (ou des valeurs), il faut passer par des méthodes intermédiaires qui récupéreront ces collections. Itérateurs: API • Les deux méthodes de base de Iterator<E> sont hasNext () et next() • remove() supprime l’élément renvoyé par le dernier appel à next() (appel de next() obligatoire avant d’appeler remove()) • Un itérateur est toujours créé par la collection LinkedList<E> l; Iterator<E> i=l.listIterator(); while(i.hasNext()){ E e= i.next(); // faire quelque chose avec e } API Complémentaire de ListIterator ListIterator<E>est un itérateur bidirectionnel • hasPrevious() et previous() • previousIndex() et nextIndex() (renvoient les index encadrant l’élément courant). • set(E) remplace l’élément courant • add(E) permet d’ajouter juste après previous, et avant next (appel de next non affecté, appel de previous changé, nextIndex et previousIndex augmentés de 1) Exemple LinkedList<Integer> l = new LinkedList<Integer>(); … ListIterator<Integer> parcours = l.listIterator(); // listIterator méthode de AbstractList<E> // parcours.next(); //exception, la liste est vide parcours.add(8); parcours.add(7); // ajoute 7 en deuxième position parcours.add(6); // ajoute 6 en troisième position parcours.previous(); parcours.add(5); // ajoute 5 en troisième position • listIterator(int) positionne l’itérateur à la valeur donnée en argument • set(E) remplace l’élément renvoyé par le dernier appel à next() ou par l’élément donné en argument Gestion de la Mémoire Pas de destructeur, mais “finalize” • La libération de la mémoire est automatique et désynchronisée • Le « garbage collector » ne peut libérer que la mémoire allouée par Java, et seulement si toutes ses strong références ont disparu • finalize est appelée par le GC avant destruction d’un objet • permet la libération de toutes les autres ressources • permet la "résurrection de l'objet" en créant une référence • On doit appeler la méthode finalize de sa super classe " void finalize () { super.finalize () ; …. } Gestion mémoire • Allocation: new: retourne une "strong reference" • Libération automatique gérée par un processus appelé garbage collector qui détecte les objets sans référence • Ce processus fonctionne en tâche de fond de faible priorité, dans un thread parallèle, mais peut être déclenché explicitement (méthode System.gc () ) – ne peut libérer que la mémoire allouée par Java – ne peut pas tout libérer malgré tout Comportement du GC et utilisation de mémoire • (d'après http://www-128.ibm.com/developerworks/java/library/jjtp11225/) Fuite de Mémoire (1) • Illustration du problème des références. Cette structure de données associe une information à un Socket, au caractère volatile. • Que se passe t'il quand un socket disparaît? • (d'après http://www-128.ibm.com/developerworks/java/library/j-jtp11225/) Fuite de mémoire (2) • (d'après http://www-128.ibm.com/developerworks/java/library/jjtp11225/) Types de Références • Trois autres types de références, en plus de la référence "strong" – weakReference (n'empêche pas le gc s'il n'y a plus de strong ref) – softReference (comme weak, mais l'objet survit tant que la mémoire le permet) • utile pour mettre en œuvre des caches – phantomReference (permet de savoir quand un objet va être détruit). C'est une alternative saine à la redéfinition de "finalize" Comment on les utilise • Un objet est “strong” s’il est accessible, sans devoir passer par une référence • Un objet est “soft” s’il n’est plus strong, mais qu’il existe une référence “soft” • Un objet est “weak” s’il n’est pas “soft”, mais qu’il existe une référence “weak” • Un objet est “phantom” s’il n’est pas “ weak”, mais qu’il existe une référence “ phantom” Queues de Références • Le constructeur d’une référence permet de passer une ReferenceQueue en paramètre. • Lorsque le GC constatera le changement de statut d’un objet, il placera la référence correspondante dans la queue déclarée. • Le programme “client” visite ses queues à intervalles réguliers, et prend les décisions appropriées java.lang.ref Reference<T> • void clear() // détruit le lien • boolean enqueue() // place dans la queue • T get() // retourne le référent (ou null si clear) • boolean isEnqueued() java.lang.ref PhantomReference<T> PhantomReference(T referent, ReferenceQueue<? super T> q) • Crée une nouvelle référence fantôme sur “referent”) • La queue est obligatoire • La fonction “get” renvoie toujours “null” SoftReference<T> WeakReference<T> • SoftReference(T referent) • SoftReference(T referent, ReferenceQueue<? super T> q) • WeakReference(T referent) • WeakReference(T referent, ReferenceQueue<? super T> q) java.lang.ref ReferenceQueue<T> • ReferenceQueue() • Reference <? extends T> poll() • Reference<? extends T> remove() • Reference<? extends T> remove (long t) • Supprime la référence suivante dans la queue, et bloque jusqu’à ce qu’une redevienne disponible ou au timeout Listing C: MyReference.java import java.lang.ref.*; public class MyReference extends SoftReference { public MyReference( Object referent ) { super( referent ); } public MyReference( Object referent, ReferenceQueue q ) { super( referent, q ); } public String toString() { return String.valueOf(get()); } } Listing B: MemoryBlock.java public class MemoryBlock { int id; int size; byte[] block; public MemoryBlock( int id, int size ) { this.id = id; this.size = size; block = new byte[size]; System.out.println( "MemoryBlock created: "+this ); } public String toString() { return "{id="+id+",size="+size+"}"; } protected void finalize() { System.out.println( "MemoryBlock finalized: "+this ); } } Listing A: ReferenceQueue example import java.lang.ref.*; import java.util.*; public class MemoryTest3 { public static void main( String[] args ) { ReferenceQueue queue = new ReferenceQueue(); ArrayList blocks = new ArrayList(); int size = 65536; for ( int id=0; true; id++ ) { blocks.add( new MyReference(new MemoryBlock(id,size),queue) ); while ( true ) { Reference ref = queue.poll(); if ( ref == null ) break; blocks.remove( ref ); } System.out.println( "blocks: "+blocks ); size *= 2; } } } http://articles.techrepublic.com.com/5100-10878_11-1049546.html# Listing D: WebCache.java import java.io.*; import java.net.*; import java.util.*; public class WebCache { WeakHashMap cache = new WeakHashMap(); public class WebObject { public String type; public byte[] content; } public WebObject get( String spec ) throws MalformedURLException,IOException { URL url = new URL(spec); WebObject obj = (WebObject) cache.get(url); if ( obj == null ) { URLConnectionconn = url.openConnection(); obj = new WebObject(); obj.type = conn.getContentType(); obj.content = new byte[conn.getContentLength()]; conn.getInputStream().read( obj.content ); cache.put( url, obj ); } return obj; } } Exemple de source • Solution: utiliser WeakHashMap à la place de HashMap: intuition du code • (d'après http://www-128.ibm.com/developerworks/java/library/jjtp11225/) Annotations Annotations • Les annotations constituent un mécanisme puissant par lequel un source peut se voir attacher des informations exploitées par le compilateur ou par des outils annexes capables de générer du code auxiliaire. • L'intérêt principal est de permettre au développeur de ne maintenir qu'un seul source. Applications • Utilisation par le compilateur pour détecter des erreurs ou ignorer des avertissements • Documentation • Génération de code - sans pouvoir modifier le code existant • Génération de fichiers API utilisant des annotations • JAXB 2.0 : JSR 222 (Java Architecture for XML Binding 2.0) • Les services web de Java 6 (JAX-WS) : JSR 181 (Web Services Metadata for the Java Platform) et JSR 224 (Java APIs for XML Web Services 2.0 API) • Les EJB 3.0 et JPA : JSR 220 (Enterprise JavaBeans) • De nombreuses API open source utilisent aussi les annotations notamment JUnit, TestNG, Hibernate, ... Définition d'annotations • Les annotations sont typées, en utilisant une variante du mot clef “interface”: “@interface”. • Exemple de définition: public @interface MyAnnotation { String doSomething(); } • Exemple d'utilisation: @MyAnnotation (doSomething="oh") public void mymethod() { .... } Trois Catégories d'annotations Les annotations appartiennent à trois catégories, selon le nombre de leurs fonctions membres: • Marker: pas de donnée – un type seul - un booléen • Single Element : un seul attribut • Multi Value : plusieurs attributs // Marker public @interface MyAnnotation {} @MyAnnotation public void mymethod() {....} Exemple Single Valued public @interface MyAnnotation { String doSomething(); } // Pas besoin de nommer le paramètre @MyAnnotation ("What to do") public void mymethod() { .... } Exemple Multi Valued • public @interface MyAnnotation { • String doSomething(); • int count(); • String date(); • } • // Utilisation : • @MyAnnotation (doSomething="What to do", count=1, date="09-09-2005") • public void mymethod() { .... } Les paramètres peuvent être passés en tableau @SuppressWarnings(value={"unchecked", "deprecation"}) et même @A1({ @A2(“Etre”), @A2(“des”), @A2(“annotations”}) Restrictions • Les méthodes ne doivent pas avoir de paramètres • Pas de clauses de lancement d'exceptions non plus • Les types de retour ne peuvent appartenir qu'à la liste suivante: • types primitifs (int, float...) • String • Class • enum • ou des tableaux des types ci dessus Annotations prédéfinies • Annotations simples: – Override – Deprecated – SuppressWarnings • Méta annotations – Target – Retention – Documented – Inherited Override • public class Test { • @Override public String toString() { • return super.toString() + "Override"; } • } • Si le nom de la fonction (toString) ne correspond pas à une méthode de la super classe, le compilateur génère une erreur Deprecated et SuppressWarnings • Deprecated permet de générer des avertissements lors de la compilation, si un programme utilise une fonction dépréciée • SuppressWarnings permet de désactiver les avertissements du compilateur Annotations communes • définies par la JSR 250 et intégrées dans Java 6 pour: • la plate-forme standard dans le package javax.annotation (@Generated, @PostConstruct, @PreDestroy, @Resource, @Resources) • la plate-forme entreprise dans le package javax.annotation.security (@DeclareRoles, @DenyAll, @PermitAll, @RolesAllowed, @RunAs). @Generated • Identifie du code généré automatiquement @Generated( value = "nom du générateur", comments = "commentaires", date = "2011" ) public void toolGeneratedCode() {...} @Resource, @Resources • Identifie les ressources utilisées par un programme • authentificationType: authentification pour utiliser la ressource • description • mappedName : nom spécifique au serveur (non portable) • name: Nom JNDI de la ressource • shareable: si la ressource est partagée • type : type pleinement qualifié de la ressource @DeclareRoles, @DenyAll, @PermitAll, @RolesAllowed, @RunAs • DeclareRoles : utilisée par l’application pour declarer les roles. • DenyAll : méthodes exclues de l’exécution dans un container J2EE • PermitAll : méthodes autorisées. • RolesAllowed : roles autorisés • RunAs : identité de l’application pour l’exécution dans un container J2EE Méta annotations • Les méta annotations annotent des annotations. • Elles sont nécessaires pour préciser: • leur durée de vie (source,class, exe) • les éléments auxquels elles s’appliquent • ... La Méta annotation « Target » • Target est une annotation d'annotation qui permet de spécifier ses emplois possibles. • @Target(ElementType.TYPE)—tout élément • @Target(ElementType.FIELD)—donnée • @Target(ElementType.METHOD) • @Target(ElementType.PARAMETER) • @Target(ElementType.CONSTRUCTOR) • @Target(ElementType.LOCAL_VARIABLE) • @Target(ElementType.ANNOTATION_TYPE) @Target (ElementType.ANNOTATION_TYPE) • @Target(ElementType.ANNOTATION_TYPE) • Indique que le type déclaré est une méta annotation (donc une annotation d’annotations. Par exemple: @Target(ElementType.ANNOTATION_TYPE) public @interface MetaAnnotationType { ... } ‘Target’ multiples • On peut grouper les paramètres des « single » @Target({ElementType.FIELD, ElementType.METHOD}) public @interface Ok { ... } • Mais on ne peut pas les répéter: @Target({ElementType.FIELD, ElementType.FIELD}) public @interface Erreur { ... } Exemple de Target @Target(ElementType.METHOD) public @interface Test_Target { public String do(); } • Utilisation correcte: @Test_Target("glop") public void methode1() {...} Retention • La méta annotation « Retention » indique la durée de vie de l'annotation: source seul, classe, machine virtuelle • RetentionPolicy.SOURCE – ignorées par le compilateur • RetentionPolicy.CLASS – utilisables par un outil qui accède aux .class • RetentionPolicy.RUNTIME – lisibles à l'exécution Documented • Indique que Javadoc doit documenter l'annotation • @Documented • public @interface Test_Documented {...} • public class TestAnnotations { • public static void main(String arg[]){} • @Test_Documented(...) public void m(){} • } • //@Test_Documented sera dans la javadoc Inherited • Par défaut, les annotations d'une classe ne sont pas héritées par les sous classes • La méta annotation « @Inherited » permet de modifier ce comportement sélectivement • Ne s 'applique qu'aux annotations attachées aux classes • L'annotation peut être surchargée, le mécanisme cherchant à partir de la classe courante • Les annotations ne sont pas héritées par les classes qui implémentent des interfaces Utilisation à l’exécution • Pour être visible à l’exécution, une annotation doit être méta annotée: @Retention(RetentionPolicy.RUNTIME) • L'interface AnnotatedElement définit les méthodes pour le traitement des annotations par introspection AnnotatedElement <T extends Annotation> getAnnotation(Class<T>) Annotation[] getAnnotations() Annotation[] getDeclaredAnnotations() • (Sauf héritées). boolean isAnnotationPresent(Class< ? extends Annotation>) • Particulièrement utile dans le traitement des annotations de type marqueur. Exemple Todo import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Todo { String[] description(); } Cette classe liste ses annotations import ...; @Todo(description = {"Modifier cette classe",”avant l’hiver”}) public class TestAnnotation { public static void main(String[] args) { Todo todo = null; // traitement d’annotation sur la classe Class classe = TestAnnotation.class; todo = classe.getAnnotation(Todo.class); if (todo != null) { System.out.println("classe "+ classe.getName()); for(String desc : todo.description()) { System.out.println(" _ "+desc); } } } // d’après http://www.jmdoudoux.fr/java/dej/chap010.htm annotations traitées à la compilation Java 6 intègre la déclaration de processeurs d’application au compilateur javac • -processor • -proc • -processorpath • -A (options aux processeurs d'annotations cle=valeur) • -XprintRounds (informations sur le traitement des annotations) • -XprintProcessorInfo liste les annotations traitées Passes (Rounds) • Le traitement des annotations se fait en plusieurs passes (round). • A chaque passe le processeur est appelé pour traiter des classes qui peuvent avoir été générées lors de la précédente passe. • Lors de la première passe, ce sont les classes fournies initialement qui sont traitées. Processeurs d’annotations • Un processeur implémente l'interface Processor. • L'interface Processor du package javax.annotation.processing définit les méthodes d'un processeur d'annotations. • Pour définir un processeur, il est possible de créer une classe qui implémente l'interface Processor mais le plus simple est d'hériter de la classe abstraite javax.annotation.processing.AbstractProcessor. AbstractProcessor • La classe AbstractProcessor contient une variable nommée processingEnv de type ProcessingEnvironment. • ProcessingEnvironment fournit les services de classes utilitaires ou qui permettent des traitements avec l'extérieur du processeur • La méthode la plus importante est la méthode process() : c'est elle qui va contenir les traitements exécutés par le processeur. Elle possède deux paramètres : • Un ensemble des annotations qui seront traitées par le processeur • Un objet qui encapsule l'étape courante des traitements Api de AbstractProcessor • Iterable<? extends Completion> getCompletions (Element element, AnnotationMirror annotation, ExecutableElement member, String userText) retourne une liste vide itérable de complétions • Set<String> getSupportedAnnotationTypes() (si le processeur est annoté avec SupportedAnnotationTypes) • Set<String> getSupportedOptions() (si le processeur est annoté avec SupportedOptions) Api de AbstractProcessor • SourceVersion getSupportedSourceVersion() (annoté avec SupportedSourceVersion) • void init(ProcessingEnvironment processingEnv) initialise le processeur • protected boolean isInitialized() • abstract boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) Traite un jeu de types d’annotations sur les éléments issus du round précédent. Si vrai est renvoyé, les annotations ne seront pas proposées aux autres processeurs. ProcessingEnvironment • (get)Filer : permet la création de fichier • (get)Messager : permet d'envoyer des messages affichés par le compilateur • (get)ElementUtils : utilitaires pour les éléments • (get) TypeUtils : utilitaires pour les types • La méthode getRootElements() renvoie les classes Java qui seront traitées par le processeur dans cette passe. Annotations des Processeurs Deux annotations sont dédiées aux processeurs d'annotations et doivent être utilisées sur la classe du processeur : • @SupportedAnnotationTypes : cette annotation permet de préciser quelles seront les types annotations traitées par le processeur. La valeur « * » permet d'indiquer que tous les types seront traités. • @SupportedSourceVersion : cette annotation permet de préciser la version du code source traité par le processeur Exemple import ...; @SupportedAnnotationTypes(value = { "*" }) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class TodoProcessor extends AbstractProcessor { @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Messager messager = processingEnv.getMessager(); for (TypeElement te : annotations) { messager.printMessage(Kind.NOTE, "Traitement annotation " " + te.getQualifiedName()); for (Element element : roundEnv.getElementsAnnotatedWith(te)) { messager.printMessage(Kind.NOTE, " Traitement element " " " + element.getSimpleName()); Todo todo = element.getAnnotation(Todo.class); if (todo != null) { messager.printMessage(Kind.NOTE, " affecte le " + todo.dateAssignation() " " + " a " + todo.assigneA()); } } } return true; } } // cf. http://www.jmdoudoux.fr/java/dej/chap010.htm Texte Dynamicité en Java? Langage Dynamique ou Statique? • Google sort DART, un langage présenté comme combinant les avantages de la dynamicité et de la vérification statique de types • Peut on avoir facilement certains services d’un langage dynamique en Java? • appel de méthode non vérifié • ajout dynamique de comportement aux instances Appel de méthodes sans interface déclarée class Foo{ public void foo(){ System.out.println("foo"); } } class Bar{ public String bar(String s){ return s; } } public static DynMethod DynMethod Foo f=new Bar b=new void main(String [] args){ foo=new DynMethod("foo"); //une fonction sans arguments bar=new DynMethod("bar",String.class); //un argument Foo(); Bar(); assert DynHelper.call(f,foo)==null; // void, affiche “foo” } System.out.println(DynHelper.call(b,bar,”bar”)); // non void, affiche “bar” assert "bar".equals(DynHelper.call(b,bar,”bar”)); Surcharge et Ajout de méthodes aux objets class Foo{ ... } class Bar{ ... } public static DynMethod DynMethod Foo f=new } void main(String [] args){ foo=new DynMethod("foo"); //une fonction sans arguments bar=new DynMethod("bar",String.class); //un argument Foo(); Bar b=new Bar(); // attach new method to object 'b' DynHelper.attach(b, new DynMethod(terMethod){ public Object run(Object ...a){System.out.println("ter"); return null;}}); DynHelper.call(b,terMethod); // void method, equivalent to b.ter() // override native behavior DynHelper.attach(b, new DynMethod(barMethod){ public Object run(Object ...a){return "quatro";}}); System.out.println(DynHelper.call(b,barMethod)); // equivalent to b.bar("") assert "quatro".equals(DynHelper.call(b,barMethod)); // cf. b.bar("") DynSignature : Descripteur de Méthodes • DynSignature encapsule simplement les données nécessaire pour l’appel de Class.getMethod(...) } class DynSignature { String name; Class<?> classes[]; DMet(String string, Class<?> this.name=string; this.classes= classes; } ...classes){ DynMethod: un pointeur de fonction • pour l’ajout dynamique de comportements public abstract class DynMethod { /* method signature */ private DynSignature signature; DynMethod(DynSignature m){this.signature=m;} /* the function pointer itself */ public abstract Object run(Object...args); } /* the signature */ public DynSignature getSignature() { return signature; } DynHelper public class DynHelper { /* Finds the Method object from its descriptor by introspection public static Method findMethod(Object o, DynSignature md){ Method m=null; try { m=o.getClass().getMethod(md.name, md.classes); } catch (SecurityException e) { rethrow (e); } catch (NoSuchMethodException e) { rethrowRE (e); } return m; } */ /* Calls the method identified by md on object o using args. */ public static Object call(Object o, DynSignature md, Object ...args) { DynMethod f=getfun(o,md); if(f!=null) return f.run(o,args); Method m=findMethod(o,md); Object ret=null; try { ret=m.invoke(o, args); } catch (IllegalArgumentException e) { rethrow (e); } catch (IllegalAccessException e) { rethrowRE (e); } catch (InvocationTargetException e) { rethrowRE (e); } return ret; } DynHelper : rethrow public static <T extends Throwable> void rethrow(T e.printStackTrace(); assert false : e.getClass().getName(); throw e; } e) throws T { public static <T extends Throwable> void rethrowRE(T e) { e.printStackTrace(); assert false : e.getClass().getName(); throw new RuntimeException(e.getClass().getName()); } DynHelper : ajouter des méthodes aux Objets /* dynamic mapping of objects to functions */ static Hashtable<Object,Hashtable<DynSignature,DynMethod>> DynamicmethodMap=new Hashtable <Object, Hashtable<DynSignature, DynMethod>>(); /* dynamically register a method on an object */ public static void attach(Object o, DynMethod f){ Hashtable<DynSignature,DynMethod> otable= DynamicmethodMap.get(o); if(otable==null) DynamicmethodMap.put(o, otable=new Hashtable<DynSignature,DynMethod>()); otable.put(f.getSignature(),f); } /* get the function registered on an object*/ public static DynMethod getfun(Object o, DynSignature name){ Hashtable<DynSignature,DynMethod> otable=DynamicmethodMap.get(o); if(otable==null) return null; return otable.get(name); } FIN Liens • http://www.javasoft.com : Site officiel Java – (JDK et documentation, téléchargement) • http://www.javaworld.com : Info sur Java • http://www-sor.inria.fr/~dedieu/java/ • Le langage Java, Touraivane, cours ESIL, Dpt GBMA • Le polycopié de C. Chaouiya, cours ESIL, Dpt GBMA • La première version de ce support de cours a été produite par Marc Daniel, ESIL • Google : http://www.google.fr/search?q=java