5. Type <?>

5.1. Introduction

Les règles d'héritage peuvent sembler curieuses lorsque l'on examine de près la façon dont les génériques fonctionnent. Une règle simple, est qu'une classe générique ne peut étendre aucune version d'elle-même. Ainsi, List<T> n'étend jamais List<U>, quelle que soit la relation qui puisse exister entre T et U. Cela dit, la classe List<T> étend bien List (le type raw ). Cela peut sembler curieux que List<Integer> ne puisse pas étendre List<Number>, alors que Integer étend lui-même Number. Si cela était le cas, nous aurions en fait de vrais problèmes, tel que celui exposé dans l'exemple suivant.

Exemple 37. Problème de sureté de type générique

// création d'une liste d'Integer
List<Integer> integerList = Arrays.asList(1,  2,  3) ;

 // ce cast n'est pas autorisé et ne compile pas, mais supposons qu'il le soit
List<Number> numberList = (List<Number>)listOfInteger ;

 // il deviendrait possible d'ajouter à integerList des éléments qui ne 
 // seraient pas des Integer !
numberList.add(1.0F) ;  // ajout d'un Float, illégal

On voit bien sur cet exemple qu'autoriser la relation d'héritage entre une liste d'entiers et une liste de nombres pose plus de problèmes que cela n'en résoud.

5.2. Type ? extension d'un type

L'introduction du type ? permet de résoudre ce problème, tout en conservant la sécurité de nos types génériques. Le principe est de pouvoir écrire une liste List<? extends Number>, qui elle, est bien étendue par List<Integer>. Comment peut-on alors protéger l'ajout d'autres éléments que des entiers dans notre liste ? Simplement en interdisant l'utilisation des méthodes prenant ? extends Number en paramètre. Utiliser la méthode add() définie sur List<? extends Number> est illégal, puisque le type pris en paramètre n'est pas entièrement connu. Ce genre de code ne compile donc pas.

Exemple 38. Utilisation du type ? extends T

List<Integer> listOfInteger = Arrays.asList(1,  2,  3) ;

 // ce cast est correct : List<? extends Number> est bien étendu 
 // par List<Integer>
List<?  extends Number> listOfNumberExtensions = listOfInteger ;

 // en revanche cet appel ne compile pas, car la méthode add de 
 // List<? extends Number> prend le type ? extends Number en paramètre, 
 // qui n'est pas connu
listOfNumberExtensions.add((Integer)2) ;

 // cet appel est correct 
 int i = listOfNumberExtensions.get(0) ;

En revanche, il est parfaitement possible de prendre Number comme type de retour pour get(int). Le véritable type est ?, mais on sait, par construction, que ce type étend Number. Dans ce cas, le type de retour du getter est garanti, en revanche celui du setter ne l'est pas. Notons que la déclaration ? extends T est appelée bounded wildcard dans les documentations en anglais.

5.3. Type ? super-type d'un type

Si l'on peut imposer que le type ? étende un type donné, on peut aussi lui imposer d'être le super-type d'un type donné, par la déclaration ? super T. Cette écriture désigne tous les types dont le type T est un type dérivé. Reprenons notre exemple précédent.

Exemple 39. Utilisation du type ? super T

List<Integer> listOfInteger = Arrays.asList(1,  2,  3) ;

 // cette déclaration n'a pas besoin de cast, car List<? super Integer> étend
 // List<Integer
List<?  super Integer> listOfNumberExtensions = listOfInteger ;

 // cet ajout est valide, on peut passer un Integer au add()
listOfNumberExtensions.add(2) ;

 // ce cast compile bien, mais n'est pas sûr, car le type de retour peut être Object
 int i = (Integer)listOfNumberExtensions.get(0) ;

Java API avancées
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
API Collection
1. Introduction
2. Interface Collection
2.1. Notion de Collection
2.2. Détail des méthodes disponibles
2.3. Interface Iterator
2.4. Implémentation, exemples d'utilisation
3. Interface List
3.1. Notion de List
3.2. Détail des méthodes disponibles
3.3. Interface ListIterator
3.4. Implémentations, exemples d'utilisation
4. Interface Set
4.1. Notion de Set
4.2. Implémentations HashSet et LinkedHashSet
4.3. Exemples d'utilisation
5. Interface SortedSet
5.1. Notion de SortedSet
5.2. Détails des méthodes disponibles
5.3. Exemples d'utilisation
6. Interface NavigableSet
6.1. Notion de NavigableSet
6.2. Détails des méthodes disponibles
6.3. Exemple d'utilisation
7. Interfaces Queue et Deque
7.1. Notion de file d'attente
7.2. Détail des méthodes disponibles
7.3. Utilisation des interfaces Queue et Deque
8. Tables de hachage
8.1. Notion de table de hachage
8.2. Interface Map
8.3. Interface Map.Entry
8.4. Interface SortedMap
8.5. Interface NavigableMap
8.6. Implémentations
8.7. Exemples d'utilisation
9. Classes utilitaires Collections et Arrays
9.1. Introduction
9.2. Classe Arrays
9.3. Classe Collections
Génériques
1. Introduction
2. Un premier exemple
2.1. Une première classe générique
2.2. Une première méthode générique
3. Contraindre un type générique
3.1. Problème posé
3.2. Contraindre un type générique
4. Implémentation des génériques
4.1. Type erasure
4.2. Types génériques et casts
4.3. Type générique et exception
4.4. Construction d'une instance générique
4.5. Génériques et membres statiques
4.6. Collisions de méthodes génériques
4.7. Implémentation de plusieurs types identiques
5. Type <?>
5.1. Introduction
5.2. Type ? extension d'un type
5.3. Type ? super-type d'un type
Expressions régulières
1. Introduction
2. Mise en œuvre des expressions régulières
2.1. Fonctionnement d'une regexp
2.2. Fonctionnement de l'API en Java
2.3. Un premier exemple
2.4. Classe Pattern
2.5. Classe Matcher
2.6. Utilisation des méthode find() et group()
2.7. Méthodes de remplacement
2.8. Sélection de régions
3. Syntaxe des expressions régulières
3.1. Notion de classe
3.2. Étude d'un cas réel
3.3. Recherche d'un mot précis
3.4. Recherche de deux mots précis
3.5. Recherche d'un mot commençant par une lettre donnée
3.6. Cas de mots comportant des caractères accentués
3.7. Recherche sur les lignes
Introspection
1. Introduction
2. La classe Class
2.1. Utilisation de Class
2.2. Méthodes disponibles
2.3. Remarque sur la propriété Accessible
2.4. Type d'une classe
2.5. Création d'une instance à partir d'un objet Class
2.6. Cas des énumérations
3. Les classes Method et Constructor
3.1. Utilisation de Method
3.2. Utilisation de Constructor
3.3. Méthodes disponibles
3.4. Invocation d'une méthode par introspection
4. La classe Field
4.1. Utilisation de Field
4.2. Méthodes disponibles
4.3. Accès à un champ par introspection
5. La classe Modifier
Programmation concurrente
1. Introduction
2. Lançons nos premiers threads
2.1. Introduction
2.2. Un premier thread, extension de Thread
2.3. Un deuxième thread, implémentation de Runnable
2.4. Remarque sur la méthode Thread.sleep(long)
2.5. Arrêter un thread
3. Concurrence d'accès
3.1. Notion d'état
3.2. Exemple de concurrence d'accès sur un état
3.3. Analyse de la concurrence d'accès
3.4. Solution au problème
3.5. Champs volatile
4. Synchronisation
4.1. Définition d'un bloc synchronisé
4.2. Fonctionnement d'un bloc synchronisé
4.3. Notion de deadlock
4.4. Bonnes pratiques pour la synchronisation de threads
5. Opérations atomiques
5.1. Atomicité d'une opération
5.2. Solutions disponibles
5.3. Variables atomiques
6. Collections synchronisées et concurrentes
6.1. Introduction
6.2. Position du problème
6.3. Solutions proposées
7. Files d'attente
7.1. Introduction, pattern producteur / consommateur
7.2. Interface BlockingQueue<E>
7.3. Implémentations de BlockingQueue
7.4. Exemple de producteur / consommateur
7.5. Arrêter un producteur / consommateur : pilule empoisonnée
8. Classes utilitaires de l'API Concurrent
8.1. Introduction
8.2. Énumération TimeUnit
8.3. Interface Callable<V>
8.4. Interfaces Future<V> et RunnableFuture<V>
8.5. Interface ScheduledFuture<V> et RunnableScheduledFuture<V>
9. Pattern executor
9.1. Notion de réserve de threads
9.2. Interface Executor
9.3. Interface ExecutorService
9.4. Interface ScheduledExecutorService
9.5. Classe Executors
9.6. Pattern de lancement de tâches
10. Classes de contrôle d'accès
10.1. Introduction
10.2. Interfaces Lock et ReadWriteLock
10.3. Notion de verrou réentrant
10.4. Classe RentrantLock
10.5. Classe ReadWriteRentrantLock
11. Sémaphores, barrières et latches
11.1. Introduction
11.2. Notion de sémaphore, classe Semaphore
11.3. Notion de latch, classe CountDownLatch
11.4. Notion de barrière, classe CyclicBarrier