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