La généricité

Transcription

La généricité
La généricité
13 avril 2016
1
Un exemple connu
La classe ArrayList permet de représenter un tableau sous forme d’un objet et de bénéficier ainsi
de nombreuses méthodes telles que l’ajout ou le retrait d’éléments. Lorsqu’on veut utiliser cette classe,
il faut préciser le type des éléments que l’on place dans le tableau.
Par exemple, si l’on veut un ArrayList contenant des String, il faudra déclarer la variable et créer
l’objet ArrayList comme suit.
ArrayList <String > var ;
v a r = new A r r a y L i s t < S t r i n g > ( ) ;
Si l’on va voir la documentation de la classe ArrayList, on voit entre autres les choses suivantes :
– Class ArrayList<E>
– boolean add(E e)
Dans le nom de la classe apparaît un E qui ne correspond pas à un type java, et ce E apparaît
également comme type de paramètres ou de résultat de méthodes de la classe. Ce E est un nom qui
peut être remplacé par n’importe quel type lorsqu’on crée effectivement un objet. Lorsque cette classe
est définie (et c’est ce qui est décrit dans la documentation), le E n’a pas de valeur précise. C’est à la
création de l’objet, lors de l’instanciation de la classe, que ce E est remplacé par le nom d’une classe
bien précise (String dans notre cas).
On appelle généricité cette possibilité de retarder la définition d’un type utilisé dans une classe.
2
Ecriture d’une classe générique
Si nous voulons écrire nous-même une classe générique, il faut reprendre à peu près la même
chose que ce que l’on voit dans la documentation de la classe ArrayList : il faut donner des noms
provisoires aux types dans le nom de la classe, avec les signes inférieur et supérieur. Après cela, les
nom provisoires peuvent être utilisés pour déclarer des variables, des paramètres, des types de résultat.
Et là, il ne faut pas mettre d’inférieur et supérieur.
Nous allons prendre l’exemple d’une mini-classe ArrayList faite maison, c’est à dire une classe
encapsulant un tableau et offrant quelques méthodes : add, set, get, remove et size.
p u b l i c c l a s s M y A r r a y L i s t <UnType >{
p r i v a t e UnType [ ] t a b ;
p r i v a t e i n t nb = 0 ;
MyArrayList ( ) {
/ / On v o u d r a i t é c r i r e i c i t a b =new UnType [ 1 0 0 ]
1
/ / mais ça n ’ e s t pas permis par l e l a n g a g e
t a b = ( UnType [ ] ) new O b j e c t [ 1 0 0 ] ;
}
p u b l i c v o i d add ( UnType e ) {
t a b [ nb ] = e ;
nb ++;
}
p u b l i c UnType s e t ( i n t i , UnType e ) {
i f ( i <0 | | i >=nb )
t h r o w new I n d e x O u t O f B o u n d s E x c e p t i o n ( ) ;
UnType r e s = t a b [ i ] ;
t a b [ i ]= e ;
return res ;
}
p u b l i c UnType g e t ( i n t i ) {
return tab [ i ] ;
}
p u b l i c UnType remove ( i n t i ) {
i f ( i <0 | | i >=nb )
t h r o w new I n d e x O u t O f B o u n d s E x c e p t i o n ( ) ;
UnType r e s = t a b [ i ] ;
f o r ( i n t j = i ; j <nb −1; j ++)
t a b [ j ]= t a b [ j + 1 ] ;
nb−−;
return res ;
}
public int size (){
r e t u r n nb ;
}
}
Ensuite, on peut utiliser la classe générique en remplaçant, à chaque objet créé, UnType par le
nom d’une classe Java.
public class Utilise {
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 ) {
M y A r r a y L i s t < S t r i n g > mal ;
mal = new M y A r r a y L i s t < S t r i n g > ( ) ;
M y A r r a y L i s t <Chose > mal2=new M y A r r a y L i s t <Chose > ( ) ;
mal . add ( " un " ) ;
mal . add ( " deux " ) ;
System . o u t . p r i n t l n ( mal . s i z e ( ) ) ;
System . o u t . p r i n t l n ( mal . remove ( 1 ) ) ;
Chose c p t = new Chose ( " r i r i " ) ;
mal2 . add ( c p t ) ;
mal2 . add ( new Chose ( " f i f i " ) ) ;
System . o u t . p r i n t l n ( mal2 . g e t ( 0 ) . nom ) ;
}
2
}
c l a s s Chose {
S t r i n g nom ;
Chose ( S t r i n g n ) {
nom=n ;
}
p u b l i c S t r i n g getNom ( ) {
r e t u r n nom ;
}
}
3
Note à propos des types génériques
Lors de l’instanciation d’une classe générique, on peut remplacer les noms de types par n’importe
quel type référence, c’est à dire un nom de classe, un type tableau, un nom d’interface, mais pas un
type primitif (int, double, boolean, char).
Si l’on essaye par exemple cette instanciation :
MyArrayList<int> mal4 = new MyArrayList<int>();
le compilateur refuse le programme avec l’erreur :
Utilise.java:16: error: unexpected type
MyArrayList<int> mal4 = new MyArrayList<int>();
^
required: reference
found:
int
Il existe quatre classes prédéfinies permettant de représenter avec des objets des entiers, nombres
à virgule, booléens et caractères : ce sont les classes Integer, Double, Boolean et Character.
On peut par exemple écrire quelque chose comme :
MyArrayList<Integer> mal5 = new MyArrayList<Integer>();
mal5.add(7);
mal5.add(12);
int x = mal5.get(0);
System.out.println(x);
4
Généricité et interface
Nous avons vu l’utilisation de généricité pour les classes. La généricité est aussi utilisée avec les
interfaces d’une façon un peu différente. En effet, les interfaces ne sont pas instanciées, ce n’est donc
pas à la création d’objet que les types inconnus sont remplacés par des types prévis. C’est lors de
l’implémentation de l’interface.
Dans la plupart des cas, on utilise le type inconnu pour représenter le type de la classe qui implémente l’interface, car on a besoin d’utiliser ce type pour les paramètres et/ou résultat des méthodes de
l’interface.
Voyons l’exemple de l’interface prédéfinie Comparable de Java. Cette interface peut s’écrire
comme suit :
3
p u b l i c i n t e r f a c e Comparable <T>{
i n t compareTo ( T o ) ;
}
Cette interface décrit la propriété d’avoir un ordre sur un type. La méthode compareTo doit comparer l’objet sur laquelle elle est appelée avec l’objet passée en paramètre et l’on veut généralement
comparer des objets de même type. Un appel à la méthode s’écrira comme suit : objet1.compareTo(objet 2).
Cette interface est implémentée par la classe prédéfinie String (la classe des chaînes de caractère)
avec comme valeur de T le type String lui-même. Dans la documentation de String on voit :
public class String implements Comparable<String>...
On voit quelque chose d’analogue avec d’autres classes prédéfinies de Java :
– public class Integer implements Comparable<Integer>
– public class Date implements Comparable<Date>
– ...
Le plus souvent, la généricité est ainsi employée dans les interfaces pour donner un nom au type
de la classe qui l’implémente.
5
Exemple d’interface générique
Nous proposons l’interface Affiliable qui propose de représenter des notions de parenté entre
objets du même type. Les méthodes fournies permettent de déclarer un lien de filiation et de tester les
liens entre deux objets. On pourra utiliser cette interface avec des êtres humains, des animaux, des
programmes objets (héritage), mais on ne pourra pas créer de lien de filiation entre un homme et un
chien ou entre une chouette et un programme : la filiation se fera uniquement entre objets de la même
classe et pour cela, on a besoin de la généricité.
p u b l i c i n t e r f a c e F i l i a t i o n <T>{
T[] getFils ();
void d e c l a r e F i l s (T f i l s ) ;
boolean e s t F i l s (T t ) ;
boolean estPere (T t ) ;
boolean estAncetre (T t ) ;
}
import java . u t i l . ArrayList ;
p u b l i c c l a s s Humain i m p l e m e n t s F i l i a t i o n <Humain >{
S t r i n g nom ;
Humain p e r e ;
A r r a y L i s t <Humain > f i l s ;
p u b l i c Humain ( S t r i n g n ) {
nom = n ;
f i l s =new A r r a y L i s t <Humain > ( ) ;
}
p u b l i c Humain [ ] g e t F i l s ( ) {
r e t u r n f i l s . t o A r r a y ( new Humain [ 0 ] ) ;
}
p u b l i c v o i d d e c l a r e F i l s ( Humain t ) {
4
i f ( t . p e r e != n u l l )
t . p e r e . f i l s . remove ( f i l s ) ;
t . pere = t h i s ;
t h i s . f i l s . add ( t ) ;
}
p u b l i c b o o l e a n e s t F i l s ( Humain t ) {
return t . pere . equals ( t h i s ) ;
}
p u b l i c b o o l e a n e s t P e r e ( Humain t ) {
return t h i s . pere . equals ( t ) ;
}
p u b l i c b o o l e a n e s t A n c e t r e ( Humain t ) {
Humain a n c e t r e = t . p e r e ;
w h i l e ( a n c e t r e ! = n u l l && a n c e t r e ! = t h i s )
ancetre = ancetre . pere ;
r e t u r n ( a n c e t r e != n u l l ) ;
}
}
5

Documents pareils