La généricité en Java (suite)
Transcription
La généricité en Java (suite)
La généricité en Java (suite) F. Barthélemy 14 mars 2006 1 Rappel : exemple simple c l a s s L i s t e <T>{ boolean e s t V i d e ( ) { return true ; } L i s t e <T> s u i t e ( ) throws L i s t e V i d e E x c e p t i o n { throw L i s t e V i d e E x c e p t i o n . exc ; } T t e t e ( ) throws L i s t e V i d e E x c e p t i o n { throw L i s t e V i d e E x c e p t i o n . exc ; } } c l a s s ListeNonVide <T> extends L i s t e <T>{ T premier ; L i s t e <T> s u i v a n t ; ListeNonVide (T p , L i s t e <T> s ){ premier = p ; suivant = s ; } boolean e s t V i d e ( ) { return f a l s e ; } L i s t e <T> s u i t e ( ) throws L i s t e V i d e E x c e p t i o n { return s u i v a n t ; } T t e t e ( ) throws L i s t e V i d e E x c e p t i o n { return p r e m i e r ; } } c l a s s L i s t e V i d e E x c e p t i o n extends Exception { s t a t i c L i s t e V i d e E x c e p t i o n exc = new L i s t e V i d e E x c e p t i o n ( ) ; } 1 c l a s s Autre { s t a t i c int l o n g u e u r ( L i s t e <?> l ) throws L i s t e V i d e E x c e p t i o n { int r e s = 0 ; while ( ! l . e s t V i d e ( ) ) { r e s ++; l = l . suite (); } return r e s ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) throws L i s t e V i d e E x c e p t i o n { L i s t e <I n t e g e r > l a l = new L i s t e <I n t e g e r > ( ) ; l a l = new ListeNonVide<I n t e g e r >(5 , l a l ) ; l a l = new ListeNonVide<I n t e g e r >(7 , l a l ) ; System . out . p r i n t l n ( l o n g u e u r ( l a l ) ) ; } } class TesteListe { public s t a t i c void main ( S t r i n g [ ] a r g s ) throws L i s t e V i d e E x c e p t i o n { L i s t e <S t r i n g > l s = new L i s t e <S t r i n g > ( ) ; l s = new ListeNonVide <S t r i n g >(” a r t ” , l s ) ; l s = new ListeNonVide <S t r i n g >(” gosh ” , l s ) ; L i s t e <S t r i n g > l t = new L i s t e <S t r i n g > ( ) ; l t = new ListeNonVide <S t r i n g >(”jam” , l t ) ; l t = new ListeNonVide <S t r i n g >( l s . t e t e ( ) , l t ) ; ls = ls . suite (); } } 2 Sécurité apportée par la généricité Première version : utilisation générique de la classe générique Liste. c l a s s ErreurTypage{ public s t a t i c void main ( S t r i n g [ ] a r g s ){ L i s t e <S t r i n g > l = new L i s t e <S t r i n g > ( ) ; l = new ListeNonVide<S t r i n g >(” o u i ” , l ) ; l = new ListeNonVide<S t r i n g >(new I n t e g e r ( 4 ) , l ) ; } } /∗ > javac Liste . java L i s t e . j a v a : 3 8 : cannot f i n d symbol symbol : c o n s t r u c t o r ListeNonVide ( j a v a . l a n g . I n t e g e r , 2 L i s t e <j a v a . l a n g . S t r i n g >) l o c a t i o n : c l a s s ListeNonVide<j a v a . l a n g . S t r i n g > l = new ListeNonVide<S t r i n g >(new I n t e g e r ( 4 ) , l ) ; ˆ 1 error ∗/ Et si l’on utilise la classe sans spécifier le paramètre générique, le code se compile, mais il y a un avertissement. c l a s s ErreurTypageBis{ public s t a t i c void main ( S t r i n g [ ] a r g s ){ L i s t e l = new L i s t e ( ) ; l = new ListeNonVide ( ” o u i ” , l ) ; l = new ListeNonVide (new I n t e g e r ( 4 ) , l ) ; } } > javac L i s t e . java Note : L i s t e . j a v a u s e s unchecked or u n s a f e o p e r a t i o n s . Note : Recompile with − X l i n t : unchecked for d e t a i l s . > j a v a c − X l i n t : unchecked L i s t e . j a v a L i s t e . j a v a : 4 4 : warning : [ unchecked ] unchecked c a l l to ListeNonVide (T, L i s t e <T>) as a member o f the raw type ListeNonVide l = new ListeNonVide ( ” o u i ” , l ) ; ˆ L i s t e . j a v a : 4 5 : warning : [ unchecked ] unchecked c a l l to ListeNonVide (T, L i s t e <T>) l = new ListeNonVide (new I n t e g e r ( 4 ) , l ) ; ˆ 2 warnings 3 Exemples de généricité dans la librairie Voici des exemples issus de la librairie. public i n t e r f a c e Comparable<T>{ int compareTo (T o ) ; } public i n t e r f a c e I t e r a b l e <T>{ I t e r a t o r <T> i t e r a t o r ( ) ; } public i n t e r f a c e I t e r a t o r <E>{ boolean hasNext ( ) ; E next ( ) ; void remove ( ) ; } public f i n a l c l a s s I n t e g e r extends Number implements Comparable<I n t e g e r >{ 3 ... public s t a t i c int bitCount ( int i ) ; public int compareTo ( I n t e g e r a n o t h e r I n t e g e r ) ; ... } public c l a s s Vector<E> extends A b s t r a c t L i s t <E> implements L i s t <E > , . . . { ... public Vector ( C o l l e c t i o n <? extends E> c ) ; ... public boolean add (E o ) ; public boolean remove ( Object o ) ; public E f i r s t E l e m e n t ( ) ; public boolean c o n t a i n s A l l ( C o l l e c t i o n <?> c ) ; ... } 4 Des exemples compliqués import j a v a . u t i l . ∗ ; c l a s s UnPeuComplique<A, B extends Vector<A>>{ B var ; UnPeuComplique (B b ){ var = b ; } void v i d e ( ) { var . c l e a r ( ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ){ Vector<I n t e g e r > v = new Vector<I n t e g e r > ( ) ; v . add ( 4 ) ; (new UnPeuComplique<I n t e g e r , Vector<I n t e g e r >>(v ) ) . v i d e ( ) ; } } c l a s s Complique<A extends Comparable<A>,B extends Vector<A>>{ void m(B leb , A l e a ){ i f ( l e a . compareTo ( l e b . f i r s t E l e m e n t ())==0){ System . out . p r i n t l n ( ” I l s s o n t egaux ” ) ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ){ Vector<I n t e g e r > v = new Vector<I n t e g e r > ( ) ; v . add ( 4 ) ; (new Complique<I n t e g e r , Vector<I n t e g e r > >()).m( v , 4 ) ; } 4 } 5 Ce qui ne marche pas On ne peut pas instancier un objet ou un tableau avec un paramètre de type. On ne peut pas non plus utiliser un paramètre de type dans une méthode ou une variable statique. c l a s s Tableau<T>{ T[ ] t ; T v; s t a t i c T v2 ; void m( ) { t = new T [ 1 0 ] ; v = new T ( ) ; } static T n(){ return null ; } } /∗ > j a v a c TabGen . j a v a TabGen . j a v a : 4 : non−s t a t i c c l a s s T cannot be r e f e r e n c e d from a s t a t i c c o n t e x t s t a t i c T v2 ; ˆ TabGen . j a v a : 9 : non−s t a t i c c l a s s T cannot be r e f e r e n c e d from a s t a t i c c o n t e x t s t a t i c T n(){ ˆ TabGen . j a v a : 6 : g e n e r i c a r r a y c r e a t i o n t = new T [ 1 0 ] ; ˆ TabGen . j a v a : 7 : u n e x p e c t e d t y p e found : t y p e parameter T required : class v = new T ( ) ; ˆ 4 errors ∗/ La solution : passer les objets et tableaux en paramètre au constructeur ou faire des contorsions. Autres choses qui ne marchent pas : les tests dynamiques. Par exemple instanceof et le cast. import j a v a . u t i l . ∗ ; c l a s s MarchePas<T>{ 5 T lameth ( Object o ){ return (T) o ; } method m( Vector v ){ i f ( v instanceof Vector<S t r i n g >){ System . out . p r i n t l n ( ”C ’ en e s t un” ) ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ){ Vector v = new Vector ( ) ; I t e r a b l e <S t r i n g > t = ( I t e r a b l e <S t r i n g >) v ; } } /∗ > j a v a c − X l i n t : unchecked MarchePas . j a v a MarchePas . j a v a : 6 : cannot f i n d symbol symbol : c l a s s method l o c a t i o n : c l a s s MarchePas<T> method m( Vector v ){ ˆ MarchePas . j a v a : 4 : warning : [ unchecked ] unchecked c a s t found : java . lang . Object required : T r e t u r n (T) o ; ˆ MarchePas . j a v a : 7 : i l l e g a l g e n e r i c t y p e f o r i n s t a n c e o f i f ( v i n s t a n c e o f Vector<S t r i n g >){ ˆ MarchePas . j a v a : 1 3 : warning : [ unchecked ] unchecked c a s t found : j a v a . u t i l . Vector r e q u i r e d : j a v a . l a n g . I t e r a b l e <j a v a . l a n g . S t r i n g > I t e r a b l e <S t r i n g > t = ( I t e r a b l e <S t r i n g >) v ; ˆ 2 errors 2 warnings ∗/ 6 Une chose surprenante Quand on donne plusieurs bornes supérieures à un paramètre de type, on ne peut pas toujours utiliser toutes les opérations de ces bornes supérieures. interface L i s i b l e { void l i t ( ) ; } interface Extensible { 6 void etend ( ) ; } abstract c l a s s B l a b l a { abstract void c a u s e ( ) ; abstract int d e g r e ( ) ; abstract B l a b l a l e P l u s B e a u ( B l a b l a b ) ; } c l a s s UneBorne<T extends Blabla >{ void m(T para ){ para . c a u s e ( ) ; para . l e P l u s B e a u ( null ) ; } } c l a s s DeuxBornes<T extends B l a b l a & L i s i b l e >{ void m(T para ){ para . l i t ( ) ; para . c a u s e ( ) ; para . l e P l u s B e a u ( para ) ; } } c l a s s DeuxBornesBis<T extends B l a b l a & L i s i b l e & E x t e n s i b l e >{ void m(T para ){ para . l i t ( ) ; para . etend ( ) ; } } /∗ > j a v a c DeuxBornes . j a v a DeuxBornes . j a v a : 2 1 : cannot f i n d symbol symbol : method cause ( ) para . cause ( ) ; ˆ DeuxBornes . j a v a : 2 2 : cannot f i n d symbol symbol : method l e P l u s B e a u (T) para . l e P l u s B e a u ( para ) ; ˆ 2 errors ∗/ Voyons les codes décompilés de ces classes : // // // // Decompiled by Jad v1 . 5 . 8 e . C o p y r i g h t 2 0 0 1 P a v e l Kouznetsov . Jad home page : h t t p : / /www . g e o c i t i e s . com/ kpdus / j a d . html Decompiler o p t i o n s : p a c k i m p o r t s ( 3 ) Source F i l e Name : DeuxBornes . j a v a c l a s s UneBorne 7 { UneBorne ( ) { } void m( B l a b l a b l a b l a ) { blabla . cause ( ) ; b l a b l a . l e P l u s B e a u ( null ) ; } } // // // // Decompiled by Jad v1 . 5 . 8 e . C o p y r i g h t 2 0 0 1 P a v e l Kouznetsov . Jad home page : h t t p : / /www . g e o c i t i e s . com/ kpdus / j a d . html Decompiler o p t i o n s : p a c k i m p o r t s ( 3 ) Source F i l e Name : DeuxBornes . j a v a c l a s s DeuxBornes { DeuxBornes ( ) { } void m( B l a b l a b l a b l a ) { (( Lis ibl e ) blabla ) . l i t ( ) ; } } // // // // Decompiled by Jad v1 . 5 . 8 e . C o p y r i g h t 2 0 0 1 P a v e l Kouznetsov . Jad home page : h t t p : / /www . g e o c i t i e s . com/ kpdus / j a d . html Decompiler o p t i o n s : p a c k i m p o r t s ( 3 ) Source F i l e Name : DeuxBornes . j a v a c l a s s DeuxBornesBis { DeuxBornesBis ( ) { } void m( L i s i b l e l i s i b l e ) { lisible . lit (); ( ( E x t e n s i b l e ) l i s i b l e ) . etend ( ) ; 8 } } 9