LU2IN002
CONVERSION DES TYPES DE VARIABLE (CAST...)
TESTS DYNAMIQUES (INSTANCEOF)
Vincent Guigue & Christophe Marsala
Exemple du logiciel de dessin
Récupérer le type d’une instance dynamiquement
Cas amusant:
le type des instances est parfois (souvent) inconnu
Exemple:
1 F i g u r e f ;
2 i f( Math . random ( ) > 0 . 5 )
3 f = new P o i n t ( 2 , 3 ) ;
4 e l s e
5 f = new Segment (new P o i n t ( 1 , 2 ) , new P o i n t ( 5 , 3 ) ) ;
6
7 // p o u r l e compilateur f e s t de t y p e F i g u r e
8 // p o u r l a JVM, q u e l e s t l e t y p e de l ’ i n s t a n c e f ? ? ?
Récupérer le type d’une instance dynamiquement
Les limites du polymorphisme sont imposées par le compilateur qui vérifie (statiquement) le type des variables
1) Contournement = connaitre le type des instances en cours de programme (dynamiquement)
2) Revenir au type de l’instance pour accéder aux méthodes spécifiques = faire un cast sur la variable
Exemple:
1 F i g u r e f ;
2 i f( Math . random ( ) > 0 . 5 )
3 f = new P o i n t ( 2 , 3 ) ;
4 e l s e
5 f = new Segment (new P o i n t ( 1 , 2 ) , new P o i n t ( 5 , 3 ) ) ;
6
7 // p o u r l e compilateur f e s t de t y p e F i g u r e
8 // p o u r l a JVM, q u e l e s t l e t y p e de l ’ i n s t a n c e f ? ? ?
1) récupération des informations : instanceof variable instanceof NomClasse
⇒ retourne un boolean
1 F i g u r e f ;
2 i f( Math . random ( ) > 0 . 5 )
3 f = new P o i n t ( 2 , 3 ) ;
4 e l s e
5 f = new Segment (new P o i n t ( 1 , 2 ) , new P o i n t ( 5 , 3 ) ) ;
6
7 i f( f i n s t a n c e o f P o i n t )
8 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ P o i n t ") ;
9 e l s e
10 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ Segment ") ;
Syntaxe particulière
Souvent, il existe d’autre moyen de faire...
Globalement, sauf exception:
instanceof = mauvaise programmation
instanceof : discussion
Procédure qui spécialise ses traitements par type de figure
1 p u b l i c s t a t i c v o i d a f f i c h e T y p e ( F i g u r e f ) {
2 i f( f i n s t a n c e o f P o i n t )
3 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ P o i n t ") ;
4 e l s e i f( f i n s t a n c e o f Segment )
5 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ P o i n t ") ;
6 e l s e i f( f i n s t a n c e o f R e c t a n g l e )
7 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ R e c t a n g l e ") ;
8 // e t c . . . un c a s p a r f i g u r e !
⇒ Très mauvais... Mais pourquoi ?
Que se passe-t-il si on ajoute un nouveau type de Figure ? Il faut toucher au code utilisateur !!!
⇒ l’outil existe, mais souvent, il faut mieux ne pas l’utiliser!
instanceof : discussion
Procédure qui spécialise ses traitements par type de figure
1 p u b l i c s t a t i c v o i d a f f i c h e T y p e ( F i g u r e f ) {
2 i f( f i n s t a n c e o f P o i n t )
3 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ P o i n t ") ;
4 e l s e i f( f i n s t a n c e o f Segment )
5 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ P o i n t ") ;
6 e l s e i f( f i n s t a n c e o f R e c t a n g l e )
7 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ R e c t a n g l e ") ;
8 // e t c . . . un c a s p a r f i g u r e !
⇒ Très mauvais... Mais pourquoi ?
Que se passe-t-il si on ajoute un nouveau type de Figure ? Il faut toucher au code utilisateur !!!
⇒ l’outil existe, mais souvent, il faut mieux ne pas l’utiliser!
instanceof: Solution
Code spécifique dans les classes:
dans Figure :
1 p u b l i c S t r i n g g e t T y p e F i g u r e ( ) { } ;
dans Point :
1 p u b l i c S t r i n g g e t T y p e F i g u r e ( ) { r e t u r n " P o i n t "; }
dans Rectangle :
1 p u b l i c S t r i n g g e t T y p e F i g u r e ( ) { r e t u r n " R e c t a n g l e "; }
Code générique (éventuellement en dehors des classes):
1 p u b l i c s t a t i c v o i d a f f i c h e T y p e ( F i g u r e f ) {
2 S y s t e m . o u t . p r i n t l n ("C ’ e s t ␣ un ␣ "+f . g e t T y p e F i g u r e ( ) ) ;
3 }
instanceof: attention à la hierarchie
1 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
2 P o i n t p1 = new P o i n t (" t o t o ", 0 , 2 ) ;
3 P o i n t p2 = new P o i n t ( " t o t o ␣ 2 ", 3 , 2 ) ;
4 F i g u r e f = new Segment ( p1 , p2 ) ;
5
6 i f( f i n s t a n c e o f P o i n t )
7 S y s t e m . o u t . p r i n t l n (" f ␣ e s t ␣ un ␣ P o i n t ") ;
8 i f( f i n s t a n c e o f Segment )
9 S y s t e m . o u t . p r i n t l n (" f ␣ e s t ␣ un ␣ Segment ") ;
10 i f( f i n s t a n c e o f F i g u r e )
11 S y s t e m . o u t . p r i n t l n (" f ␣ e s t ␣ une ␣ F i g u r e ") ;
12 }
Le programme suivant retourne:
1 f e s t un Segment
2 f e s t une F i g u r e
Le résultat est logique: il faut comprendre instanceof comme «EST UN?»
Alternative à instanceof: getClass() public Class getClass()
Méthode de Object
S’utilise sur une instance (syntaxe différente de instanceof) Retourne la classe (complète) de l’instance
Unique pour une classe donnée
Accès à différents descripteurs de la classe
1 F i g u r e f = new Segment ( p1 , p2 ) ;
2 S y s t e m . o u t . p r i n t l n (" f ␣ e s t ␣ de ␣ t y p e ␣ : ␣ "+f . g e t C l a s s ( ) ) ;
3 // r e t o u r :
4 // f e s t de t y p e : c l a s s c o u r s 2 . Segment
Usage classique pour comparer le type de deux instances:
1 // s o i t d e u x O b j e t s o b j 1 e t o b j 2
2 i f ( o b j 1 . g e t C l a s s ( ) != o b j 2 . g e t C l a s s ( ) )
3 . . .
2) modifier le type d’une variable : Cast Cast = 2 modes de fonctionnement
Conversion sur les types basiques: le codage des données change. Souvent implicite dans votre codage...
1 d o u b l e d = 1 . 4 ;
2 i n t i = (i n t) d ; // i =1
Conversion dans les hiérarchies de classes:
la variable est modifiée, l’instance est inchangée
1 F i g u r e f = new Segment ( p1 , p2 ) ;
2 Segment s = ( Segment ) f ;
3 // Pour v e r i f i e r que v o u s a v e z c o m p r i s :
4 // d o n n e r un diagramme memoire
Utile pour accéder aux méthodes spécifiques d’une instance
Dangereux: aucun contrôle du compilateur...
Cast: limite
Un système peu sécurisé à la compilation:
1 P o i n t p1 = new P o i n t (" t o t o ", 0 , 2 ) ;
2 P o i n t p2 = new P o i n t ( " t o t o ␣ 2 ", 3 , 2 ) ;
3 F i g u r e f = new Segment ( p1 , p2 ) ;
4
5 F i g u r e f 2 = ( F i g u r e ) p1 ;
// OK m a i s i n u t i l e
6 F i g u r e f 3 = p1 ; // OK S u b s o m p t i o n c l a s s i q u e
7 Segment s = ( Segment ) f ; // c o m p i l a t i o n OK
8 P o i n t p3 = ( P o i n t ) f ; // c o m p i l a t i o n OK ( ! )
Exécution:
Crash du programme avec le message suivant
1 E x c e p t i o n i n t h r e a d " main " j a v a . l a n g . C l a s s C a s t E x c e p t i o n :
2 c o u r s 2 . Segment c a n n o t be c a s t t o c o u r s 2 . P o i n t
3 a t c o u r s 2 . T e s t . main ( T e s t . j a v a : 2 6 )
Cast: limite
Un système peu sécurisé à la compilation:
1 P o i n t p1 = new P o i n t (" t o t o ", 0 , 2 ) ;
2 P o i n t p2 = new P o i n t ( " t o t o ␣ 2 ", 3 , 2 ) ;
3 F i g u r e f = new Segment ( p1 , p2 ) ;
4
5 F i g u r e f 2 = ( F i g u r e ) p1 ; // OK m a i s i n u t i l e
6 F i g u r e f 3 = p1 ; // OK S u b s o m p t i o n c l a s s i q u e
7 Segment s = ( Segment ) f ;
// c o m p i l a t i o n OK
8 P o i n t p3 = ( P o i n t ) f ; // c o m p i l a t i o n OK ( ! )
Exécution:
Crash du programme avec le message suivant
1 E x c e p t i o n i n t h r e a d " main " j a v a . l a n g . C l a s s C a s t E x c e p t i o n :
2 c o u r s 2 . Segment c a n n o t be c a s t t o c o u r s 2 . P o i n t
3 a t c o u r s 2 . T e s t . main ( T e s t . j a v a : 2 6 )
Cast: limite
Un système peu sécurisé à la compilation:
1 P o i n t p1 = new P o i n t (" t o t o ", 0 , 2 ) ;
2 P o i n t p2 = new P o i n t ( " t o t o ␣ 2 ", 3 , 2 ) ;
3 F i g u r e f = new Segment ( p1 , p2 ) ;
4
5 F i g u r e f 2 = ( F i g u r e ) p1 ; // OK m a i s i n u t i l e
6 F i g u r e f 3 = p1 ; // OK S u b s o m p t i o n c l a s s i q u e
7 Segment s = ( Segment ) f ; // c o m p i l a t i o n OK
8 P o i n t p3 = ( P o i n t ) f ;
// c o m p i l a t i o n OK ( ! )
Exécution:
Crash du programme avec le message suivant
1 E x c e p t i o n i n t h r e a d " main " j a v a . l a n g . C l a s s C a s t E x c e p t i o n :
2 c o u r s 2 . Segment c a n n o t be c a s t t o c o u r s 2 . P o i n t
3 a t c o u r s 2 . T e s t . main ( T e s t . j a v a : 2 6 )
Cast: limite
Un système peu sécurisé à la compilation:
1 P o i n t p1 = new P o i n t (" t o t o ", 0 , 2 ) ;
2 P o i n t p2 = new P o i n t ( " t o t o ␣ 2 ", 3 , 2 ) ;
3 F i g u r e f = new Segment ( p1 , p2 ) ;
4
5 F i g u r e f 2 = ( F i g u r e ) p1 ; // OK m a i s i n u t i l e
6 F i g u r e f 3 = p1 ; // OK S u b s o m p t i o n c l a s s i q u e
7 Segment s = ( Segment ) f ; // c o m p i l a t i o n OK
8 P o i n t p3 = ( P o i n t ) f ; // c o m p i l a t i o n OK ( ! )
Exécution:
Crash du programme avec le message suivant
1 E x c e p t i o n i n t h r e a d " main " j a v a . l a n g . C l a s s C a s t E x c e p t i o n :
2 c o u r s 2 . Segment c a n n o t be c a s t t o c o u r s 2 . P o i n t
3 a t c o u r s 2 . T e s t . main ( T e s t . j a v a : 2 6 )
Cast: avec plusieurs niveaux de hiérarchie
1 // s u b s o m p t i o n
2 F i g u r e f = new Segment ( p1 , p2 ) ;
3 F i g u r e f 2 = new C a r r e ( p1 , c o t e ) ;
4 F i g u r e f 3 = new T r i a n g l e ( p1 , p2 , p3 ) ; ;
5 P o l y g o n e p = new T r i a n g l e ( p4 , p5 , p6 ) ;
6
7 // c a s t OK
8 T r i a n g l e t = ( T r i a n g l e ) p ; // OK ( comme p r e c e d e m m e n t )
9
10 P o l y g o n e p2 = ( P o l y g o n e ) f 2 ; // OK c o m p i l + e x e c :
11 // un C a r r e EST UN P o l y g o n e
12
13 P o l y g o n e p3 = ( T r i a n g l e ) f 3 ; // OK:
14 // f 3 e s t un T r i a n g l e => c o n v e r s i o n OK (JVM)
15 // p3 p e u t r e f e r e n c e r un T r i a n g l e OK ( c o m p i l . )
16
17 // c a s t KO
18 C a r r e c = ( C a r r e ) f ; // KO JVM
19 C e r c l e c = ( C e r c l e ) p ; // KO Compil: opération impossible
Cast: avec plusieurs niveaux de hiérarchie
1 // s u b s o m p t i o n
2 F i g u r e f = new Segment ( p1 , p2 ) ;
3 F i g u r e f 2 = new C a r r e ( p1 , c o t e ) ;
4 F i g u r e f 3 = new T r i a n g l e ( p1 , p2 , p3 ) ; ;
5 P o l y g o n e p = new T r i a n g l e ( p4 , p5 , p6 ) ;
6
7 // c a s t OK
8 T r i a n g l e t = ( T r i a n g l e ) p ; // OK ( comme p r e c e d e m m e n t )
9
10 P o l y g o n e p2 = ( P o l y g o n e ) f 2 ; // OK c o m p i l + e x e c :
11 // un C a r r e EST UN P o l y g o n e
12
13 P o l y g o n e p3 = ( T r i a n g l e ) f 3 ; // OK:
14 // f 3 e s t un T r i a n g l e => c o n v e r s i o n OK (JVM)
15 // p3 p e u t r e f e r e n c e r un T r i a n g l e OK ( c o m p i l . )
16
17 // c a s t KO
18 C a r r e c = ( C a r r e ) f ; // KO JVM
19 C e r c l e c = ( C e r c l e ) p ; // KO Compil: opération impossible
Cast: sécurisation Idée
Vérifier le type de l’instance avant la conversion
1 F i g u r e f = new Segment ( p1 , p2 ) ;
2 Segment s ;
3 i f( f i n s t a n c e o f Segment )
4 s = ( Segment ) f ;
⇒ vous utiliserez systématiquement cette sécurisation
Que penser de:
1 // F i g u r e −> P o l y g o n e −> C a r r e
2 F i g u r e f = new C a r r e ( c o t e ) ;
3 P o l y g o n e p ;
4 i f( f i n s t a n c e o f P o l y g o n e )
5 p = ( P o l y g o n e ) f ;
Est ce que ça marche (compil+exec)?
Est ce que ça peut être utile?
Cast: sécurisation Idée
Vérifier le type de l’instance avant la conversion
1 F i g u r e f = new Segment ( p1 , p2 ) ;
2 Segment s ;
3 i f( f i n s t a n c e o f Segment )
4 s = ( Segment ) f ;
⇒ vous utiliserez systématiquement cette sécurisation Que penser de:
1 // F i g u r e −> P o l y g o n e −> C a r r e
2 F i g u r e f = new C a r r e ( c o t e ) ;
3 P o l y g o n e p ;
4 i f( f i n s t a n c e o f P o l y g o n e )
5 p = ( P o l y g o n e ) f ;
Est ce que ça marche (compil+exec)?
Est ce que ça peut être utile?
Cast: usage dans equals fonction equals
Il y a toujours un cast (sécurisé) dans equals pour pouvoir accéder aux attributs à comparer
Exemple sur la classe Point:
1 p u b l i c b o o l e a n e q u a l s ( O b j e c t o b j ) { // V1
2 i f (t h i s == o b j )
3 r e t u r n t r u e;
4 i f ( o b j == n u l l)
5 r e t u r n f a l s e;
6 i f ( g e t C l a s s ( ) != o b j . g e t C l a s s ( ) )
7 r e t u r n f a l s e;
8 P o i n t o t h e r = ( P o i n t ) o b j ;
9 i f ( x != o t h e r . x )
10 r e t u r n f a l s e;
11 i f ( y != o t h e r . y )
12 r e t u r n f a l s e;
13 r e t u r n t r u e;
14 }
getClass vs instanceof
Imaginons la redéfinition suivante pour equals:
1 p u b l i c b o o l e a n e q u a l s ( O b j e c t o b j ) {// V2
2 i f (t h i s == o b j )
3 r e t u r n t r u e;
4 i f ( o b j == n u l l)
5 r e t u r n f a l s e;
6 // i f ( g e t C l a s s ( ) != o b j . g e t C l a s s ( ) ) en V1
7 i f ( ! ( o b j instanceof P o i n t ) )
8 r e t u r n f a l s e;
9 P o i n t o t h e r = ( P o i n t ) o b j ;
10 i f ( x != o t h e r . x )
11 r e t u r n f a l s e;
12 i f ( y != o t h e r . y )
13 r e t u r n f a l s e;
14 r e t u r n t r u e;
15 }
Quel est le défaut de l’implémentation v2?
Prise en défaut:
1 P o i n t p = new P o i n t ( 1 , 2 ) ;
2 P o i n t T r a f i q u e p2 = new P o i n t T r a f i q u e (" t o t o ", 1 , 2 ) ;
3 i f( p . e q u a l s ( p2 ) )
4 S y s t e m . o u t . p r i n t l n (" i l s ␣ s o n t ␣ e g a u x ␣ ! ! ! ␣ ") ;
5 i f( p2 . e q u a l s ( p ) )
6 S y s t e m . o u t . p r i n t l n (" e t ␣ i c i ␣ ? ? ? ") ;
Avec :
1 p u b l i c c l a s s P o i n t T r a f i q u e e x t e n d s P o i n t {
2 p r i v a t e S t r i n g qqch ;
3 p u b l i c P o i n t T r a f i q u e ( S t r i n g nom , d o u b l e x , d o u b l e y ) {
4 s u p e r( x , y ) ;
5 qqch = nom ; // un a t t r i b u t en p l u s
6 }
7 }