2. Un premier exemple

2.1. Une première classe générique

Une classe générique est une classe Java qui peut être réutilisée pour des objets de différents types. Prenons l'exemple de la classe ArrayList de Java 4, qui permet de stocker des objets dans une liste. Cette classe expose entre autres une méthode get(), et une méthode add(Object).

Exemple 18. Classe ArrayList de Java 4

public  class ArrayList {

    public Object get() { ... }
   
    public  void add(Object object) { ... }

}

Cette classe a été utilisée dans d'innombrables applications, mais en toute rigueur, on peut lui faire deux reproches :
  • À chaque fois que l'on lit un objet d'une liste, on doit le caster dans le bon type. Par exemple, pour une liste de String, on devra écrire (String)list.get().
  • La classe n'impose aucun contrôle lorsque l'on ajoute des objets. Si, par erreur, on introduit d'autres objets que des String dans une telle liste, on ne pourra s'en rendre compte qu'à la lecture du contenu de la liste.
C'est pour pallier ces problèmes que les génériques ont été introduits en Java : pouvoir déclarer qu'une liste contient des String, pouvoir détecter dès la compilation que l'on est en train d'ajouter d'autres éléments que des String dans notre liste, et éviter d'avoir à faire des cast lors de la lecture. En utilisant les génériques, on peut écrire notre classe ArrayList de la façon suivante.

Exemple 19. Classe ArrayList de Java 5

public  class ArrayList<T> {

    public T get() { ... }
   
    public  void add(T t) { ... }

}

Dans cette écriture, T est le type générique. Lors de l'instanciation de cette classe, on doit lui donner une valeur, qui doit être une classe ou une interface. Dans le cas où T représente le type String, notre classe devient :

Exemple 20. Un ArrayList de String

public  class ArrayList<String> {

    public String get() { ... }
   
    public  void add(String s) { ... }

}

Voyons un exemple de déclaration d'une telle variable.

Exemple 21. Instanciation d'un ArrayList générique

private ArrayList<String> listOfString =  new ArrayList<String>() ;

 private ArrayList<Integer> listOfInteger =  new ArrayList<Integer>() ;

On voit sur cette classe que la méthode add() prend une String en paramètre. Ajouter un objet d'un autre type mènera bien à une erreur de compilation, ce qui était notre objectif. De plus, la méthode get retourne également une String, ce qui va nous éviter d'utiliser un cast à chaque lecture. On voit également que l'on peut, de la même façon, créer une liste d'autres type que String, Integer par exemple.

2.2. Une première méthode générique

On peut également déclarer un type générique à une méthode, que cette méthode appartienne à une classe générique ou non. La déclaration est la suivante.

Exemple 22. Déclaration d'une méthode générique

public  class ArrayUtils {

    public  static <T> T getFirst(T[] arrayOfT) { ... }
}

La déclaration du type générique doit se faire en déclarant ce type entre brackets après les modificateurs de méthode (ici public static). Voyons la syntaxe générale d'appel d'une telle méthode.

Exemple 23. Appel d'une méthode générique

String first = ArrayUtils.<String>getFirst("Surcouf",  "Auguin",  "Tabarly") ;

Comme on le voit, la déclaration du type doit se faire avant l'écriture du nom de la méthode. Dans la pratique, et dans notre exemple c'est le cas, cette déclaration est optionnelle, car la machine Java saura deviner ce type. Mais ce n'est pas toujours le cas, déclarer ce type systématiquement est donc une bonne pratique.
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