2. • En
Java,
un
thread
est
un
objet
d’une
classe
qui
dispose
d’une
méthode
nommée
run
qui
sera
exécutée
lorsque
le
thread
sera
démarré.
• Il
existe
deux
façons
de
définir
une
telle
classe.
– créer
une
classe
dérivée
de
la
classe
Thread
– Implémenter
l’interface
Runnable
3. Exemple
introduc2f
class
Ecrit
extends
Thread
{
public
Ecrit(String
texte,
int
nb){
this.texte
=
texte
;
this.nb
=
nb
;
}
public
void
run
()
{
for(inO=0;i<nb;i++)
System.out.print
(texte)
;
}
}
4. Exemple
introduc2f
La
créaOon
des
objets
threads
pourra
se
faire,
depuis
n’importe
quel
endroit
du
programme
(par
exemple
depuis
une
méthode
main)
de
ceUe
façon
:
Ecrit
e1
=
new
Ecrit
("bonjour",
10)
;
Ecrit
e2
=
new
Ecrit
("bonsoir",
12)
;
Ecrit
e3
=
new
Ecrit
("n",
5)
;
Le
lancement
de
l’exécuOon
du
thread
se
fait
en
appelant
la
méthode
start
de
la
classe
Thread,
par
exemple
:
e1.start()
;
//
lance
l’exécu2on
du
thread
e1
les
textes
apparaissent
plus
ou
moins
entremêlés
à
l’écran.
5. Exemple
introduc2f
• nous
uOliserons
l’appel
sleep(t)
où
t
est
un
nombre
de
millisecondes.
• Cet
appel
demande
que
le
thread
correspondant
soit
arrêté
(on
dira
"mis
en
sommeil")
pour
au
moins
la
durée
menOonnée.
• CeUe
démarche
laisse
ainsi
la
possibilité
à
d’autres
threads
de
s’exécuter
à
leur
tour.
• La
méthode
sleep
est
suscepOble
de
générer
une
excepOon
de
type
InterruptedExcep0on.
8. Remarques
• Un
programme
comporte
toujours
au
moins
un
thread
dit
"thread
principal"
correspondant
tout
simplement
à
la
méthode
main.
Ici,
notre
programme
comporte
donc
quatre
threads
et
non
trois.
Lorsque
la
méthode
sleep
est
appelée,
elle
permet
de
donner
la
main
à
l’un
des
autres
threads,
y
compris
le
thread
principal.
• Si
l’on
appelait
directement
la
méthode
run
de
nos
objets
threads,
le
programme
foncOonnerait
mais
l’on
n’aurait
plus
affaire
à
trois
threads
différents.
On
exécuterait
alors
enOèrement
la
méthode
run
du
premier,
puis
celle
du
deuxième
et
enfin
celle
du
troisième,
tout
cela
dans
un
seul
thread.
9. Remarques
• La
méthode
start
ne
peut
être
appelée
qu’une
seule
fois
pour
un
objet
thread
donné.
Dans
le
cas
contraire,
on
obOendra
une
excepOon
IllegalThreadStateExcep8on.
• La
méthode
sleep
est
en
fait
une
méthode
staOque
(de
la
classe
Thread
)
qui
met
en
sommeil
le
thread
en
cours
d’exécuOon.
Nous
aurions
pu
remplacer
l’appel
sleep
(a;ente)
par
Thread.sleep
(a;ente).
• Si
nous
ne
prévoyons
pas
d’appel
de
sleep
dans
notre
méthode
run,
le
programme
foncOonnera
encore
mais
son
comportement
dépendra
de
l’environnement.
10. U2lisa2on
de
l’interface
Runnable
• Nous
venons
de
voir
comment
créer
des
threads
à
parOr
de
la
classe
Thread.
• CeUe
démarche
est
simple
mais
elle
présente
une
lacune
:
les
objets
threads
ne
peuvent
pas
dériver
d’autre
chose
que
de
Thread
(puisque
Java
ne
possède
pas
d’héritage
mulOple).
• Un
thread
peut
être
créé
grâce
à
une
classe
implémentant
l’interface
Runnable,
qui
comporte
une
seule
méthode
nommée
run.
12. Exemple
• Nous
serons
amenés
à
créer
des
objets
de
type
Ecrit,
par
exemple
:
Ecrit
e1
=
new
Ecrit
("bonjour
",
10,
5)
;
• CeUe
fois,
ces
objets
ne
sont
plus
des
threads
et
ne
peuvent
donc
plus
être
lancés
par
la
méthode
start.
• Nous
devrons
tout
d’abord
créer
des
objets
de
type
Thread
en
uOlisant
une
forme
parOculière
de
constructeur
recevant
en
argument
un
objet
implémentant
l’interface
Runnable,
par
exemple
:
Thread
t1
=
new
Thread
(e1)
;
• Nous
lancerons
ensuite
classiquement
ce
thread
par
start
:
t1.start()
;
14. Interrup2on
d’un
thread
• Dans
certains
cas,
on
peut
avoir
besoin
d’interrompre
prématurément
un
thread
depuis
un
autre
thread.
• Ce
besoin
peut
devenir
fondamental
dans
le
cas
de
ce
que
nous
nommerons
des
"threads
infinis",
c’est-‐à-‐dire
dans
lesquels
la
méthode
run
n’a
pas
de
fin
programmée
;
• ce
pourrait
être
le
cas
d’un
thread
de
surveillance
d’appels
dans
un
serveur
Web.
15. Interrup2on
d’un
thread
• Java
dispose
d’un
mécanisme
permeUant
à
un
thread
d’en
interrompre
un
autre.
• La
méthode
interrupt
de
la
classe
Thread
demande
à
l’environnement
de
posiOonner
un
indicateur
signalant
une
demande
d’arrêt
du
thread
concerné.
18. Interrup2on
d’un
thread
• La
méthode
staOque
interrupted
de
la
classe
Thread
remet
à
false
l’indicateur
de
demande
d’arrêt.
• La
classe
Thread
dispose
également
d’une
méthode
(non
staOque)
isInterrupted
qui
examine
l’indicateur
de
l’objet
thread
correspondant
(et
non
plus
du
thread
courant),
sans
en
modifier
la
valeur.
19. Threads
démons
et
arrêt
brutal
• il
existe
deux
catégories
de
threads
:
– les
threads
dits
uOlisateur,
– les
threads
dits
démons.
• La
parOcularité
d’un
thread
démon
est
la
suivante
:
–
si
à
un
moment
donné,
les
seuls
threads
en
cours
d’exécuOon
d’un
même
programme
sont
des
démons,
ces
derniers
sont
arrêtés
brutalement
et
le
programme
se
termine.
20. Threads
démons
et
arrêt
brutal
• Par
défaut,
un
thread
est
créé
dans
la
catégorie
du
thread
qui
l’a
créé
(uOlisateur
pour
main,
donc
pour
tous
les
threads,
tant
qu’on
n’a
rien
demandé
d’autre).
•
Pour
faire
d’un
thread
un
démon,
on
effectue
l’appel
setDaemon(true)
avant
d’appeler
la
méthode
start
(si
on
le
fait
après
ou
si
l’on
appelle
plusieurs
fois
setDaemon,
on
obOent
une
excepOon
InvalidThread-‐StateExcep8on).
21. Coordina2on
de
threads
• L’avantage
des
threads
sur
les
processus
est
qu’ils
apparOennent
à
un
même
programme.
• Ils
peuvent
donc
éventuellement
partager
les
mêmes
objets.
•
Cet
avantage
s’accompagne
parfois
de
contraintes:
– il
faudra
éviter
que
deux
threads
puissent
accéder
(presque)
en
même
temps
au
même
objet.
– un
thread
devra
aUendre
qu’un
autre
ait
achevé
un
certain
travail
sur
un
objet
avant
de
pouvoir
lui-‐
même
poursuivre
son
exécuOon.
22. Coordina2on
de
threads
:
exemple
• Prenons
un
exemple
simple
de
deux
threads
répétant
indéfiniment
les
acOons
suivantes
:
– incrémentaOon
d’un
nombre
et
calcul
de
son
carré
(premier
thread),
– affichage
du
nombre
et
de
son
carré
(second
thread).
• On
voit
que
si
le
premier
thread
se
trouve
interrompu
entre
l’incrémentaOon
et
le
calcul
de
carré,
le
second
risque
d’afficher
le
nouveau
nombre
et
l’ancien
carré.
• Pour
pallier
ceUe
difficulté,
Java
permet
de
déclarer
des
méthodes
avec
le
mot-‐clé
synchronized.
• À
un
instant
donné,
une
seule
méthode
ainsi
déclarée
peut
être
appelée
pour
un
objet
donné.
26. Coordina2on
de
threads
:
No2on
de
verrou
• À
un
instant
donné,
une
seule
méthode
synchronisée
peut
donc
accéder
à
un
objet
donné.
• Pour
meUre
en
place
une
telle
contrainte,
on
peut
considérer
que,
pour
chaque
objet
doté
d’au
moins
une
méthode
synchronisée,
l’environnement
gère
un
"verrou"
(ou
une
clé)
unique
permeUant
l’accès
à
l’objet.
• Le
verrou
est
aUribué
à
la
méthode
synchronisée
appelée
pour
l’objet
et
il
est
resOtué
à
la
sorOe
de
la
méthode.
Tant
que
le
verrou
n’est
pas
resOtué,
aucune
autre
méthode
synchronisée
ne
peut
le
recevoir
(bien
sûr,
les
méthodes
non
synchronisées
peuvent,
quant
à
elles,
accéder
à
tout
moment
à
l’objet).
28. Coordina2on
de
threads
:
Interblocage
• L’uOlisaOon
des
verrous
sur
des
objets
peut
conduire
à
une
situaOon
de
blocage
qui
peut
se
définir
ainsi
:
– le
thread
t1
possède
le
verrou
de
l’objet
o1
et
il
aUend
le
verrou
de
l’objet
o2,
– le
thread
t2
possède
le
verrou
de
l’objet
o2
et
il
aUend
le
verrou
de
l’objet
o1.
29. Coordina2on
de
threads
:
ATente
et
no2fica2on
• Comme
nous
l’avons
dit,
il
arrive
que
l’on
ait
besoin
de
coordonner
l’exécuOon
de
threads,
• Un
thread
devant
aUendre
qu’un
autre
ait
effectué
une
certaine
tâche
pour
conOnuer
son
exécuOon.
30. Coordina2on
de
threads
:
ATente
et
no2fica2on
• Java
offre
un
mécanisme
basé
sur
l’objet
et
sur
les
méthodes
synchronisées
que
nous
venons
d’étudier
:
– une
méthode
synchronisée
peut
appeler
la
méthode
wait
de
l’objet
dont
elle
possède
le
verrou,
ce
qui
a
pour
effet
:
• de
rendre
le
verrou
à
l’environnement
qui
pourra,
le
cas
échéant,
l’aUribuer
à
une
autre
méthode
synchronisée,
• de
meUre
"en
aUente"
le
thread
correspondant
;
plusieurs
threads
peuvent
être
en
aUente
sur
un
même
objet
;
tant
qu’un
thread
est
en
aUente,
l’environnement
ne
lui
donne
pas
la
main
;
– une
méthode
synchronisée
peut
appeler
la
méthode
no8fyAll
d’un
objet
pour
prévenir
tous
les
threads
en
aUente
sur
cet
objet
et
leur
donner
la
possibilité
de
s’exécuter.
31. Coordina2on
de
threads
:
Exercice
1
• Ecrire
un
programme
qui
gère
une
"réserve"
(de
tout
ce
qui
se
dénombre).
Il
comporte
:
– un
thread
qui
ajoute
une
quanOté
donnée,
– deux
threads
qui
puisent
chacun
une
quanOté
donnée.
• Un
thread
ne
peut
puiser
dans
la
réserve
que
si
elle
conOent
une
quanOté
suffisante.
• La
réserve
est
représentée
par
un
objet
r,
de
type
Reserve.
• CeUe
classe
dispose
de
deux
méthodes
synchronisées
puise
et
ajoute.
• Lorsque
la
méthode
puise
s’aperçoit
que
la
réserve
est
insuffisante,
il
appelle
wait
pour
meUre
le
thread
correspondant
en
aUente.
Parallèlement,
la
méthode
ajoute
appelle
no8fyAll
après
chaque
ajout.
32. Coordina2on
de
threads
:
Exercice
2
• Dans
l’exemple
de
calcul
et
d’affichage
du
carré,
les
deux
threads
calc
et
aff
n’étaient
pas
coordonnés
;
on
pouvait
incrémenter
plusieurs
fois
le
nombre
avant
qu’il
n’y
ait
affichage
ou,
encore,
afficher
plusieurs
fois
les
mêmes
informaOons.
• Ici,
nous
allons
faire
en
sorte
que,
malgré
leurs
rythmes
différents,
les
deux
threads
soient
coordonnés,
c’est-‐à-‐dire
qu’on
effectue
alternaOvement
une
incrémentaOon
et
un
calcul.
34. IntroducOon
• On
parle
généralement
de
programmaOon
générique
lorsqu’un
langage
permet
d’écrire
un
code
source
unique
uOlisable
avec
des
objets
ou
des
variables
de
types
quelconques.
• On
peut
prendre
l’exemple
d’une
méthode
de
tri
applicable
à
des
objets
de
type
quelconque
ou
encore
celui
d’une
classe
permeUant
de
manipuler
des
ensembles
d’objets
de
type
quelconque.
35. No2on
de
classe
générique
• La
définiOon
d’une
classe
générique
pourrait
se
présenter
ainsi
:
36. No2on
de
classe
générique:
déclara2on
• On
note
la
présence
d’un
"paramètre
de
type"
nommé
ici
T,
dans
:
class
Couple<T>
• Il
sert
à
préciser
que,
dans
la
définiOon
de
classe
qui
suit,
T
représente
un
type
quelconque.
• Ce
paramètre
T
peut
alors
être
uOlisé
là
où
un
type
précis
peut
l’être
normalement.
•
Ici,
on
le
rencontre
:
– dans
les
déclaraOons
des
champs
x
et
y,
– dans
l’en-‐tête
du
constructeur
et
de
la
méthode
getPremier.
37. No2on
de
classe
générique:
u2lisa2on
• Lors
de
la
déclaraOon
d’un
objet
de
type
Couple,
on
devra
préciser
le
type
effecOf
correspondant
à
T,
de
ceUe
manière
:
– Couple<Integer>ci;
– Couple<Point>
cp;
• La
seule
contrainte
à
respecter
à
ce
niveau
est
que
ce
type
doit
obligatoirement
être
une
classe
;
•
la
déclaraOon
suivante
serait
rejetée
:
– Couple
<int>
c
;
//
erreur
:
int
n’est
pas
une
classe
• L’appel
du
constructeur
devra
également
préciser
le
type
voulu.
Par
exemple,
si
l’on
dispose
de
deux
objets
oi1
et
oi2
de
type
Integer,
on
créera
le
couple
correspondant
par
:
– ci
=
new
Couple<Integer>
(oi1,
oi2)
;
38. No2on
de
classe
générique:
compila2on
• Considérons
notre
classe
Couple
<T>.
Sa
compilaOon
conduit
à
créer
les
mêmes
"byte
codes"
que
si
nous
l’avions
définie
ainsi
(en
supprimant
la
déclaraOon
de
paramètre
de
type
<T>
et
en
remplaçant
T
par
Object
dans
la
suite)
:
39. No2on
de
classe
générique:
compila2on
• Dans
l’uOlisaOon
de
la
classe
générique
Couple<T>,
nous
avions
déclaré
:
Couple
<Integer>
ci
;
• Lorsque
le
compilateur
rencontre
un
appel
tel
que
:
ci.getPremier()
• il
le
traduit
en
insérant
une
conversion
du
type
Object
dans
le
type
Integer.
En
effet,
à
ce
niveau
:
– le
compilateur
sait
que,
à
cause
de
l’effacement,
lors
de
l’exécuOon,
le
résultat
fourni
par
getPremier
sera
de
type
Object,
– mais
il
connaît
quand
même
le
type
de
ci,
grâce
à
sa
déclaraOon.
En
définiOve,
tout
se
passera
comme
si
vous
aviez
écrit
votre
appel
de
ceUe
façon
:
(Integer)
ci.getPremier()
40. No2on
de
classe
générique
• Il
est
possible,
au
moment
de
la
définiOon
de
la
classe,
d’imposer
certaines
contraintes.
• On
pourra
imposer
à
la
classe
correspondant
à
un
paramètre
de
type
d’être
dérivée
d’une
classe
donnée
ou
d’implémenter
une
ou
plusieurs
interfaces.
• Par
exemple:
class
Couple
<T
extends
Number>
{
//
définiOon
précédente
inchangée
}
• on
imposera
au
type
désigné
par
T
de
dériver
de
la
classe
Number
41. No2on
de
classe
générique:
limita2ons
• On
ne
peut
pas
instancier
un
objet
d’un
type
paramétré
42. No2on
de
classe
générique:
limita2ons
• On
ne
peut
pas
instancier
de
tableaux
d’éléments
d’un
type
générique
• Seul
le
type
brut
est
connu
lors
de
l’exécuOon
43. No2on
de
classe
générique:
limita2ons
• ExcepOons
– Il
n’est
pas
possible
de
créer
une
classe
générique
dérivée
de
Throwable,
donc
a
forOori
de
Excep8on
ou
de
Error
:
– Il
n’est
pas
possible
de
lever
une
excepOon
(throw)
à
l’aide
d’un
objet
d’une
classe
générique
:
44. No2on
de
classe
générique:
limita2ons
• Champs
staOques
– Si
l’on
définit
un
champ
staOque
dans
une
classe
générique,
il
sera
unique
pour
toutes
les
instances
de
ceUe
classe,
quelle
que
soit
la
valeur
du
paramètre
de
type.
– Un
champ
staOque
ne
peut
pas
être
d’un
type
paramétré
:
45. No2on
de
méthodes
générique
• Nous
venons
de
voir
comment
on
pouvait
introduire
des
paramètres
de
type
dans
une
classe.
• La
même
démarche
peut
s’appliquer
à
une
méthode
;
on
parle
alors
tout
naturellement
de
"méthodes
génériques".
46. No2on
de
méthodes
générique:
exemple
• Là
encore,
la
compilaOon
de
la
méthode
générique
conduit
à
l’effacement
du
type
T,
exactement
comme
si
l’on
avait
défini
hasard
de
ceUe
façon
:
47. No2on
de
méthodes
générique:
exemple
• Si
l’on
souhaite
davantage
de
vérificaOons
à
la
compilaOon,
il
est
possible
d’imposer
le
type
voulu
pour
T
lors
de
l’appel
de
la
méthode,
en
uOlisant
une
syntaxe
de
la
forme
suivante
:
nomClasse<type>.nomMéthode
48. Héritage
et
programma2on
générique
• Disposant
d’une
classe
générique
telle
que
:
class
C
<T>
{
.....
}
• il
existe
bon
nombre
de
façons
d’en
créer
des
classes
dérivées
:
– La
classe
dérivée
conserve
les
paramètres
de
type
de
la
classe
de
base,
sans
en
ajouter
d’autres,
comme
dans
:
class
D
<T>
extends
C
<T>
{
.....
}
class
D<T,
U>
extends
C<T,
U>
– Ici,
C
et
D
uOlisent
le
même
paramètre
de
type.
49. Héritage
et
programma2on
générique
• La
classe
dérivée
uOlise
les
mêmes
paramètres
de
type
que
la
classe
de
base,
en
en
ajoutant
de
nouveaux,
comme
dans
:
class
D
<T,
U>
extends
C
<T>
{
.....
}
• La
classe
dérivée
introduit
des
limitaOons
sur
un
ou
plusieurs
des
paramètres
de
type
de
la
classe
de
base,
comme
dans
:
class
D
<T
extends
Number>
extends
C<T>
50. Héritage
et
programma2on
générique
• La
classe
de
base
n’est
pas
générique,
la
classe
dérivée
l’est,
comme
dans
(on
suppose
que
X
est
une
classe)
:
class
D<T>
extends
X
• La
classe
de
base
est
une
instance
parOculière
d’une
classe
générique,
comme
dans
:
class
D<T>
extends
C<String>
• En
revanche,
ces
situaOons
seront
incorrectes
:
class
D
extends
C<T>
//erreur:
D
doit
disposer
au
moins
du
paramètre
T
class
G<T>
extends
C<T
extends
Number>
//
erreur
51. Les
jokers
• si
T’
dérive
de
T,
C<T’>
ne
dérive
pas
de
C<T>.
• il
existe
une
relaOon
intéressante
entre
ces
deux
classes
puisqu’il
reste
toujours
possible
d’uOliser
un
objet
de
type
C<T’>
comme
on
le
ferait
d’un
objet
de
type
C<T>,
52. Les
jokers
• Avec
notre
classe
C<T>
précédente,
nous
pouvons
bien
entendu
définir
:
C<Integer>
ci
;
C<Double>
cd
;
• Mais,
on
peut
également
définir
:
C<?>cq;
//cq
désigne
un
couple
d’éléments
d’un
type
quelconque
• Certes,
ceUe
déclaraOon
ressemble
à
:
C<Object>
cq1
;
• Mais,
la
grande
différence
est
que,
par
exemple,
ceUe
affectaOon
devient
légale
cq
=
cd
;
//
OK
:
affecta2on
d’un
C<Double>
à
un
C<?>
• alors
que
celle-‐ci
ne
le
serait
pas
:
cq1
=
cd
;
//
erreur
de
compila2on
• En
revanche,
il
n’est
pas
possible
de
modifier
l’objet
référence
par
cq
:
cq.setT(.....);
//erreur
de
compilaOon
53. Les
jokers
• ce
ne
sont
pas
simplement
les
modificaOons
d’un
objet
de
type
C<?>
qui
sont
interdites,
mais
plus
précisément,
l’appel
de
toute
méthode
recevant
un
argument
du
type
correspondant
à
?.
• Par
exemple,
supposons
qu’on
ait
muni
notre
classe
C
d’une
méthode
permeUant
de
comparer
un
aUribut
x
à
un
objet
fourni
en
argument
•
54. Les
jokers
• On
peut
imposer
des
limitaOons
à
un
joker,
comme
on
le
fait
pour
des
paramètres
de
type.
•
Ainsi,
avec
notre
classe
C
nous
pouvons
définir
:
C
<Object>
co
;
C
<Integer>
ci
;
C
<?
extends
Number>
cqn
;
//
?
représente
un
type
quelconque
dérivé
de
Number
• L’affectaOon
suivante
sera
illégale
:
cqn
=
co
;
//
erreur
de
compila2on
:
Object
ne
dérive
pas
de
Number
• tandis
que
celle-‐ci
sera
légale
:
cqn
=
ci
;
//
OK
:
Integer
dérive
bien
de
Number
56. Introduc2on
• La
version
2
de
Java
a
élargi
et
harmonisé
la
bibliothèque
de
classes
uOlitaires
(java.u8l).
• On
y
trouve
désormais
des
classes
permeUant
de
manipuler
les
principales
structures
de
données,
c’est-‐à-‐dire
les
vecteurs
dynamiques,
les
ensembles,
les
listes
chaînées,
les
piles
et
les
tables
associaOves.
57. Les
collec2ons
• les
collec2ons
sont
manipulées
par
le
biais
de
classes
génériques
implémentant
l’interface
Collec0on<E>,
E
représentant
le
type
des
éléments
de
la
collecOon.
• Tous
les
éléments
d’une
même
collecOon
sont
donc
de
même
type
E
(ou,
à
la
rigueur,
d’un
type
dérivé
de
E).
• Ainsi,
à
une
liste
chaînée
LinkedList<String>,
on
ne
pourra
pas
ajouter
des
éléments
de
type
Integer
ou
Point.
58. Ordre
des
éléments
d’une
collecOon
• Par
nature,
certaines
collecOons,
comme
les
ensembles,
sont
dépourvues
d’un
quelconque
ordonnancement
de
leurs
éléments.
• D’autres,
en
revanche,
comme
les
vecteurs
dynamiques
ou
les
listes
chaînées
voient
leurs
éléments
naturellement
ordonnés
suivant
l’ordre
dans
lequel
ils
ont
été
disposés.
• Dans
de
telles
collecOons,
on
pourra
toujours
parler,
à
un
instant
donné,
du
premier
élément,
du
deuxième,
...
du
nième,
du
dernier.
59. Ordre
des
éléments
d’une
collecOon
• Indépendamment
de
cet
ordre
naturel,
on
pourra,
dans
certains
cas,
avoir
besoin
de
classer
les
éléments
à
par2r
de
leur
valeur.
• Ce
sera
par
exemple
le
cas
d’un
algorithme
de
recherche
de
maximum
ou
de
minimum
ou
encore
de
tri.
• Lorsqu’il
est
nécessaire
de
disposer
d’un
tel
ordre
sur
une
collecOon,
les
méthodes
concernées
considèrent
par
défaut
que
ses
éléments
implémentent
l’interface
Comparable
(Comparable<E>
depuis
le
JDK
5.0)
et
recourent
à
sa
méthode
compareTo
60. Ordre
des
éléments
d’une
collecOon
• Il
se
peut
que
la
démarche
précédente
(uOlisaOon
de
compareTo)
ne
convienne
pas.
• Ce
sera
notamment
le
cas
lorsque
:
– les
éléments
sont
des
objets
d’une
classe
existante
qui
n’implémente
pas
l’interface
Comparable,
– on
a
besoin
de
définir
plusieurs
ordres
différents
sur
une
même
collecOon.
61. Ordre
des
éléments
d’une
collecOon
• Pour
ce
faire,
on
fournit
en
argument
(du
constructeur
ou
de
l’algorithme)
un
objet
qu’on
nomme
un
comparateur.
• Il
s’agit
en
fait
d’un
objet
d’un
type
implémentant
l’interface
Comparator<E>1
(ou
Comparator
avant
le
JDK
5.0)
qui
comporte
une
seule
méthode
:
– public
int
compare
(E
o1,
E
o2)
//
depuis
le
JDK
5.0
– public
int
compare
(Object
o1,
Object
o2)
//
avant
le
JDK
5.0
62. égalité
des
éléments
d’une
collecOon
• Toutes
les
collec2ons
nécessitent
de
définir
l’égalité
de
deux
éléments.
• Ce
besoin
est
évident
dans
le
cas
des
ensembles
(HashSet
et
TreeSet)
dans
lesquels
un
même
élément
ne
peut
apparaître
qu’une
seule
fois.
63. Ordre
des
éléments
d’une
collecOon
• CeUe
égalité
est
définie
en
recourant
à
la
méthode
equals
de
l’objet.
• Pour
des
éléments
de
type
String,
File
ou
d’une
classe
enveloppe,
les
choses
seront
naturelles
puisque
leur
méthode
equals
se
base
réellement
sur
la
valeur
des
objets.
• Pour
les
autres,
par
défaut,
leur
méthode
equals
est
celle
héritée
de
la
classe
Object.
• Elle
se
base
simplement
sur
les
références
:
deux
objets
différents
apparaîtront
toujours
comme
non
égaux
(même
s’ils
conOennent
exactement
les
mêmes
valeurs).
• Pour
obtenir
un
comportement
plus
saOsfaisant,
il
faudra
alors
redéfinir
la
méthode
equals
de
façon
appropriée.
64. Les
itérateurs
et
leurs
méthodes
• Les
itérateurs
sont
des
objets
qui
permeUent
de
"parcourir"
un
par
un
les
différents
éléments
d’une
collecOon
• Il
existe
deux
sortes
d’itérateurs
:
– monodirec0onnels
:
le
parcours
de
la
collecOon
se
fait
d’un
début
vers
une
fin
;
on
ne
passe
qu’une
seule
fois
sur
chacun
des
éléments
;
– bidirec0onnels
:
le
parcours
peut
se
faire
dans
les
deux
sens
;
on
peut
avancer
et
reculer
à
sa
guise
dans
la
collecOon.
65. Les
itérateurs
monodirec2onnels
:
l’interface
Iterator
• Chaque
classe
collecOon
dispose
d’une
méthode
nommée
iterator
fournissant
un
itérateur
monodirecOonnel,
c’est-‐à-‐dire
un
objet
d’une
classe
implémentant
l’interface
Iterator<E>
(Iterator
avant
le
JDK
5.0).
• Associé
à
une
collecOon
donnée,
il
possède
les
propriétés
suivantes
:
– À
un
instant
donné,
un
itérateur
indique
ce
que
nous
nommerons
une
posi0on
courante
désignant
soit
un
élément
donné
de
la
collecOon,
soit
la
fin
de
la
collecOon
– On
peut
obtenir
l’objet
désigné
par
un
itérateur
en
appelant
la
méthode
next
de
l’itérateur,
ce
qui,
en
outre,
avance
l’itérateur
d’une
posiOon.
– La
méthode
hasNext
de
l’itérateur
permet
de
savoir
si
l’itérateur
est
ou
non
en
fin
de
collecOon
67. Les
itérateurs
monodirec2onnels
:
l’interface
Iterator
• La
méthode
remove
de
l’interface
Iterator
• Notez
bien
que
remove
ne
travaille
pas
directement
avec
la
posiOon
courante
de
l’itérateur,
mais
avec
la
dernière
référence
renvoyée
par
next
que
nous
nommerons
objet
courant.
Alors
que
la
posiOon
courante
possède
toujours
une
valeur,
l’objet
courant
peut
ne
pas
exister.
68. Les
itérateurs
bidirec2onnels
:
l’interface
Iterator
• Les
itérateurs
bidirec2onnels
:
l’interface
ListIterator
• Certaines
collecOons
(listes
chaînées,
vecteurs
dynamiques)
peuvent,
par
nature,
être
parcourues
dans
les
deux
sens.
• Elles
disposent
d’une
méthode
nommée
listIterator
qui
fournit
un
itérateur
bidirecOonnel.
• Il
s’agit,
ceUe
fois,
d’objet
d’un
type
implémentant
l’interface
ListIterator<E>
(dérivée
de
Iterator<E>).
• Il
dispose
bien
sûr
des
méthodes
next,
hasNext
et
remove
héritées
de
Iterator.
Mais
il
dispose
aussi
d’autres
méthodes
permeUant
d’exploiter
son
caractère
bidirecOonnel,
à
savoir
:
– comme
on
peut
s’y
aUendre,
des
méthodes
previous
et
hasPrevious,
complémentaires
de
next
et
hasNext,
– mais
aussi,
des
méthodes
d’addiOon1
d’un
élément
à
la
posiOon
courante
(add)
ou
de
modificaOon
de
l’élément
courant
(set).
69. Les
itérateurs
bidirec2onnels
:
l’interface
Iterator
• L’interface
ListIterator
prévoit
une
méthode
add
qui
ajoute
un
élément
à
la
posiOon
courante
de
l’itérateur.
• Si
ce
dernier
est
en
fin
de
collecOon,
l’ajout
se
fait
tout
naturellement
en
fin
de
collecOon
(y
compris
si
la
collecOon
est
vide).
• Si
l’itérateur
désigne
le
premier
élément,
l’ajout
se
fera
avant
ce
premier
élément.
70. Les
itérateurs
bidirec2onnels
:
l’interface
Iterator
• L’appel
set
(elem)
remplace
par
elem
l’élément
courant,
c’est-‐à-‐dire
le
dernier
renvoyé
par
next
ou
previous,
à
condiOon
que
la
collecOon
n’ait
pas
été
modifiée
entre
temps
(par
exem-‐
ple
par
add
ou
remove).
•
N’oubliez
pas
que
les
éléments
ne
sont
que
de
simples
références
;
la
modificaOon
opérée
par
set
n’est
donc
qu’une
simple
modificaOon
de
référence
(les
objets
concernés
n’étant
pas
modifiés).
• N’oubliez
pas
que
set,
comme
remove,
s’applique
à
un
élément
courant
(et
non
comme
add
à
une
posiOon
courante).
• Par
exemple,
si
it
est
un
itérateur
bidirecOonnel,
la
séquence
suivante
est
incorrecte
et
provoquera
une
excepOon
IllegalStateExcep8on
:
it.next()
;
it.remove()
;
it.set
(el)
;
72. Opéra2ons
communes
à
toutes
les
collec2ons
• Opéra2ons
communes
à
toutes
les
collec2ons
• Ajout
73. Opéra2ons
communes
à
toutes
les
collec2ons
• Opéra2ons
communes
à
toutes
les
collec2ons
• La
méthode
add
fournit
la
valeur
true
lorsque
l’ajout
a
pu
être
réalisé,
ce
qui
sera
le
cas
avec
la
plupart
des
collecOons,
excepOon
faite
des
ensembles
;
dans
ce
cas,
on
obOent
la
valeur
false
si
l’élément
qu’on
cherche
à
ajouter
est
déjà
"présent"
dans
l’ensemble.
• De
la
même
façon,
toute
collecOon
dispose
d’une
méthode
remove
(element)
qui
recherche
un
élément
de
valeur
donnée
et
le
supprime
s’il
existe
en
fournissant
alors
la
valeur
true.
• La
méthode
size
fournit
la
taille
d’une
collecOon,
c’est-‐à-‐dire
son
nombre
d’éléments
tandis
que
la
méthode
isEmpty
teste
si
elle
est
vide
ou
non.
La
méthode
clear
supprime
tous
les
éléments
d’une
collecOon.
• La
méthode
contains
(elem)
permet
de
savoir
si
la
collecOon
conOent
un
élément
de
valeur
égale
à
elem
76. Les
listes
chaînées
-‐
classe
LinkedList
• LinkedList
dispose
des
méthodes
spécifiques
getFirst
et
getLast
fournissant
respecOvement
le
premier
ou
le
dernier
élément
de
la
liste.
77. Les
listes
chaînées
-‐
classe
LinkedList
• La
méthode
add
de
ListIterator
(ne
la
confondez
pas
avec
celle
de
Collec8on)
permet
d’ajouter
un
élément
à
la
posiOon
courante.
• la
méthode
add
prévue
dans
l’interface
Collec8on
reste
uOlisable.
Elle
se
con-‐
tente
d’ajouter
l’élément
en
fin
de
liste.
• la
méthode
remove
de
ListIterator
supprime
le
dernier
élément
renvoyé
soit
par
next
• La
classe
LinkedList
dispose
en
outre
de
méthodes
spécifiques
removeFirst
et
removeLast
qui
suppriment
le
premier
ou
le
dernier
élément
de
la
liste.
• comme
pour
toute
collecOon,
supprimer
d’une
liste
un
élément
de
valeur
donnée
avec
remove
(element).
78. Les
vecteurs
dynamiques
-‐
classe
ArrayList
• Comme
toute
collecOon,
les
vecteurs
disposent
de
la
méthode
add
(elem)
qui
se
contente
d’ajouter
l’élément
elem
en
fin
de
vecteur.
• On
peut
aussi
ajouter
un
élément
elem
en
un
rang
i
donné
à
l’aide
de
la
méthode
add
(i,elem).
• La
classe
ArrayList
dispose
d’une
méthode
spécifique
remove
permeUant
de
supprimer
un
élément
de
rang
donné
• On
peut
connaître
la
valeur
d’un
élément
de
rang
i
par
get(i).
• On
peut
remplacer
par
elem
la
valeur
de
l’élément
de
rang
i
par
set
(i,
elem).
79. Les
ensembles
• Deux
classes
implémentent
la
noOon
d’ensemble
:
HashSet
et
TreeSet.
• Théoriquement,
un
ensemble
est
une
collecOon
non
ordonnée
d’éléments,
aucun
élément
ne
pouvant
apparaître
plusieurs
fois
dans
un
même
ensemble.
• HashSet
qui
recourt
à
une
technique
dite
de
hachage,
ce
qui
conduit
à
une
efficacité
du
test
d’appartenance
en
O(1),
• TreeSet
qui
uOlise
un
arbre
binaire
pour
ordonner
complètement
les
éléments,
ce
qui
conduit
à
une
efficacité
du
test
d’appartenance
en
O(Log
N).
• La
seule
façon
d’ajouter
un
élément
à
un
ensemble
est
d’uOliser
la
méthode
add
prévue
dans
l’interface
Collec8on.
80. Les
queues
• Le
JDK
5.0
a
introduit
une
nouvelle
interface
Queue
(dérivée
elle
aussi
de
Collec8on),
desOnée
à
la
gesOon
des
files
d’aUente
(ou
queues).
• Il
s’agit
de
structures
dans
lesquelles
on
peut
:
–
introduire
un
nouvel
élément,
si
la
queue
n’est
pas
pleine,
– prélever
le
premier
élément
de
la
queue,
• Le
prélèvement
du
premier
élément
de
la
queue
peut
se
faire
:
– de
façon
destrucOve,
à
l’aide
de
la
méthode
poll
:
l’élément
ainsi
prélevé
est
supprimé
de
la
queue
;
la
méthode
renvoie
null
si
la
queue
est
vide,
– de
façon
non
destrucOve
à
l’aide
de
la
méthode
peek.
81. Les
queues
• Deux
classes
implémentent
l’interface
Queue
:
– La
classe
LinkedList,
–
La
classe
PriorityQueue,
introduite
par
Java
5,
permet
de
choisir
une
relaOon
d’ordre
;
le
type
des
éléments
doit
implémenter
l’interface
Comparable
ou
être
doté
d’un
comparateur
approprié.
Les
éléments
de
la
queue
sont
alors
ordonnés
par
ceUe
relaOon
d’ordre
et
le
prélèvement
d’un
élément
porte
alors
sur
le
"premier"
au
sens
de
ceUe
relaOon
(on
parle
du
"plus
prioritaire",
d’où
le
nom
de
PriorityQueue).
82. Les
tables
associa2ves
• Une
table
associaOve
permet
de
conserver
une
informaOon
associant
deux
parOes
nommées
clé
et
valeur.
• On
va
donc
tout
naturellement
retrouver
les
deux
types
d’organisaOon
rencontrés
pour
les
ensembles
:
– table
de
hachage
:
classe
HashMap,
– arbre
binaire
:
classe
TreeMap.
87. Les
bases
de
l’introspec2on
:
le
type
Class
• Jusqu’ici,
nous
avons
manipulé
des
objets
qui
étaient
des
instances
d’une
classe.
• Mais
Java
permet
également
de
manipuler
des
classes,
qu’il
considère
alors
comme
des
objets
d’un
"super-‐type"
nommé
Class.
• Nous
verrons
bientôt
qu’un
tel
super-‐
type
dispose
de
méthodes
fournissant
des
informaOons
sur
ses
instances
qui
sont
des
"super-‐
objets
»
88. Les
bases
de
l’introspec2on
:
le
type
Class
• DéclaraOon
d’instances
du
type
Class
– Avant
Java
5,
:
Class
c
;
– Depuis
Java
5,
le
type
Class
est
devenu
générique,
de
sorte
qu’il
doit
être
paramétré
:
• Class<Point>
c
;
(nous
verrons
toutefois
que
ceUe
démarche
doit
généralement
être
évitée)
;
• Class<?>
c
;
• Class<?
extends
Point>
c
;
89. Les
bases
de
l’introspec2on
:
le
type
Class
• Nous
pouvons
exploiter
le
fait
que
toute
classe
T
dispose
d’un
champ
staOque
public,
nommé
class,
fournissant
le
"super-‐objet"
de
type
Class
correspondant
à
ceUe
classe
T
• Class
c<?>
;
//
ou,
avant
Java
5
:
Class
c
;
• c
=
Point.class
;
• affecte
à
c
le
super-‐objet
représentant
la
classe
Point.
90. Les
bases
de
l’introspec2on
:
le
type
Class
• Toute
classe
dispose
d’une
méthode
getClass
qui
permet
de
retrouver
la
classe
d’une
instance
donnée.
• Point
p
;
•
c
=
p.getClass()
;
• on
affecte
à
c,
ce
même
super-‐objet
représentant
la
classe
Point.
91. Les
bases
de
l’introspec2on
:
le
type
Class
• Toute
classe
dispose
d’une
méthode
getClass
qui
permet
de
retrouver
la
classe
d’une
instance
donnée.
• Point
p
;
•
c
=
p.getClass()
;
• on
affecte
à
c,
ce
même
super-‐objet
représentant
la
classe
Point.
92. Les
bases
de
l’introspec2on
:
le
type
Class
• La
classe
Class
dispose
d’une
méthode
nommée
getName
fournissant
le
nom
(String)
d’un
super-‐
objet,
donc
d’une
classe.
• Avec
notre
précédent
exemple
:
Class<?>c;
Pointp;
.....
c
=
p.getClass()
;
• l’expression
c.getName()
aurait
comme
valeur,
la
chaîne
"Point".
93. Les
bases
de
l’introspec2on
:
le
type
Class
• Exemple
94. Les
bases
de
l’introspec2on
:
le
type
Class
• Exemple
95. Accès
aux
informa2ons
rela2ves
à
une
classe
• Généralités
:
les
types
Field,
Method
et
Constructor
• voyons
comment
nous
pouvons
en
extraire
des
informaOons,
à
l’aide
des
méthodes
du
type
Class
(définies
dans
le
package
Java.lang.reflect)
• D’une
manière
générale,
ces
méthodes
uOlisent
:
– des
objets
de
type
Field
pour
représenter
un
champ,
– des
objets
de
type
Method
pour
représenter
une
méthode
(constructeurs
non
compris),
–
des
objets
de
type
générique
Constructor
<T>
(Constructor
avant
le
JDK
5)
pour
représenter
un
constructeur.
96. Accès
aux
informa2ons
rela2ves
à
une
classe
• la
méthode
getDeclaredFields
fournit
un
tableau
d’objets
de
type
Field
correspondant
aux
champs
déclarés.
La
méthode
GetFields
fournirait
tous
les
champs
publics,
y
compris
ceux
hérités.
Field[]
champs
=
c.getDeclaredFields()
;
• la
méthode
getDeclaredMethods
fournit
un
tableau
d’objets
de
type
Method:
• Method[]
methodesd
=
c.getDeclaredMethods
()
;
• La
méthode
getMethods
fournirait
toutes
les
méthodes
publiques,
y
compris
celles
héritées.
97. Accès
aux
informa2ons
rela2ves
à
une
classe
• la
méthode
getDeclaredConstructors
fournit
un
tableau
d’objets
de
type
Constructor,
• Constructor
<?>[]
constructeurs
=
c.getDeclaredConstructors()
;
• La
méthode
getConstructors
fournirait
seulement
les
constructeurs
publics.
98. Accès
aux
informa2ons
rela2ves
à
une
classe
• Dans
la
classe
Field,
on
trouve,
en
plus
de
getName,
un
certain
nombre
de
méthodes
permeUant
d’obtenir
des
informaOons
sur
le
champ
concerné
– la
méthode
getType
fournit
le
type
du
champ,
sous
forme
d’un
objet
de
type
Class<?>
;
on
notera
que
les
types
de
base
disposent
eux-‐aussi
d’un
tel
type,
par
exemple
Class<int>
ou
Class<double>;
– la
méthode
getModifiers
fournit
un
enOer
dont
la
valeur
dépend
des
modificateurs
(public,
private,
protected,
sta8c...)
;
–
la
classe
Modifier
propose
des
méthodes
(isPublic,
isPrivate,
isSta8c...)
permeUant
de
savoir
si
un
modificateur
donné
est
présent
dans
l’enOer
précédent.
99. Accès
aux
informa2ons
rela2ves
à
une
classe
• Dans
les
classes
Method
et
Construtor,
on
trouve,
en
plus
de
getName,
un
certain
nombre
de
méthodes
permeUant
d’obtenir
des
informaOons
sur
la
méthode
concernée.
– la
méthode
getModifiers
fournit
un
enOer
dont
la
valeur
dépend
des
modificateurs
(public,
private,
protected,
sta8c,
synchronized,
final...)
;
– la
classe
Modifier
propose
des
méthodes
(isPublic,
isPrivate,
isSta8c...)
permeUant
de
sa-‐
voir
si
un
modificateur
donné
est
présent
dans
l’enOer
précédent
;
– la
méthode
getReturnType
fournit
un
objet
de
type
Class<?>
correspondant
au
type
de
la
valeur
de
retour
(bien
entendu,
ceUe
méthode
n’existe
pas
dans
la
classe
Constructor)
;
– la
méthode
getParameterTypes
fournit
un
tableau
d’objet
de
type
Class<?>
correspondant
aux
types
des
différents
arguments
;
– la
méthode
getExcep8onType
fournit
un
tableau
d’éléments
de
type
Class
<?>
correspondant
aux
types
des
excepOons
levées
par
la
méthode.
100. Les
annota2ons
• Voyons
comment
définir
et
uOliser
une
annotaOon
simple
(ne
comportant
pas
de
paramètres)
qui
sera
donc
simplement
caractérisée
par
le
nom
que
nous
lui
donnerons,
ici
Marque.
Sa
définiOon
se
présentera
comme
ceci
:
• public
@interface
Marque
{}
• Elle
ressemble
à
celle
d’une
interface
(en
uOlisant
le
terme
@interface
au
lieu
de
interface),
avec
un
statut
(public,
private,
protected
ou
rien),
un
nom
et
un
corps
102. Les
annota2ons
• L’annotaOon
de
l’exemple
précédent
était
très
simple
car
réduite
à
une
simple
marque.
Mais
une
annotaOon
peut
disposer
de
un
ou
plusieurs
paramètres
(appelés
aussi
"aUributs").
Voyez
cet
exemple
:
public
@interface
InformaOons
{
String
message
()
;
int
annee
()
;
}
103. Les
annota2ons
• Il
suffira
de
lui
fournir
les
valeurs
des
paramètres
requis
de
ceUe
manière
:
@InformaOons
(message
=
"code
provisoire",
annee
=
2007)
public
class
A
{
.....
}
•
on
peut
fournir
des
valeurs
par
défaut
pour
tout
ou
parOe
des
paramètres,
comme
dans
:
public
@interface
InformaOons
{
String
message
()
default
""
;
//
message
vide
par
défaut
int
annee
()
;
//
pas
de
valeur
par
défaut
}
104. Exploita2on
des
annota2ons
par
introspec2on
• isAnnota8onPresent
qui
teste
si
une
annotaOon
donnée
(considérée
comme
une
classe)
fournie
en
argument
est
présente,
par
exemple
:
• if
(A.class.isAnnotaOonPresent
(Infos.class))
• examine
si
l’annotaOon
@Infos
(classe
de
nom
Infos)
est
présente
sur
la
classe
A
;
105. Exploita2on
des
annota2ons
par
introspec2on
• getAnnota8on
qui
reçoit
en
argument
la
classe
de
l’annotaOon
recherchée
et
qui
fournit
en
résultat
un
objet
du
type
de
l’annotaOon
en
quesOon
;
•
par
exemple,
si
m1
est
un
objet
de
type
Method,
avec
:
• Infos
ainf1
=
m1.getAnnotaOon(Infos.class)
;
• getAnnota8ons
qui
fournit
un
tableau
d’objets
de
type
Annota8on
correspondant
aux
annotaOons
de
l’élément
concerné,
y
compris
celles
héritées,
• getDeclaredAnnota8ons
qui
fournit
les
annotaOons
effecOvement
déclarées.