Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Prochaines sessions inter-entreprises : 28-31 mars 2017 / 13-16 juin 2017
Sessions intra-entreprises sur demande.
Inscrivez-vous vite !

Java Quiz #27

Que pensez-vous de ce code?
(Quiz proposé par Grégory Boissinot)

  1. class Animal {}
  2.  
  3. class Dog extends Animal {}
  4.  
  5. class Main
  6. {
  7. public static void printAnimals(List<Animal> animals)
  8. { ... }
  9.  
  10. public static void printAnimals(Animal[] animals)
  11. { ... }
  12.  
  13. public static void main(String args[])
  14. {
  15. Dog gromit = new Dog();
  16. Dog[] dogArray = {gromit};
  17. printAnimals(dogArray);
  18. List<Dog> dogList=new ArrayList<Dog>();
  19. dogList.add(gromit);
  20. printAnimals(dogList);
  21. }
  22. }

Réponse :

Ce code ne compile pas.
Le compilateur indique que l'appel de la méthode printAnimals() de la ligne 7 est illégal, puisqu'on lui passe un paramètre de type List<Dog>, alors qu'elle attend un paramètre de type List<Animal>.

Vous objecterez que "Dog extends Animal" et que le même appel avec un tableau ne semble pas poser de problème. Voyons ces deux points.

Le cas des collections

Imaginons un instant que l'appel de la méthode printAnimals() de la ligne 7 avec un paramètre de type List<Dog> soit valide.
En imaginant également l'existence d'une classe "Cat extends Animal", dans le corps de la méthode, nous pourrions écrire ceci :

  1. public static void printAnimals(List<Animal> animals)
  2. {
  3. animals.add(new Cat());
  4. }

Voyez-vous le problème ? Nous venons juste d'ajouter un Cat dans une liste de Dog, au mépris du typage de la collection ! Car, à l'intérieur de la méthode, le compilateur ne voit que le type déclaré du paramètre, à savoir List<Animal>, et il estime parfaitement légal d'ajouter un Cat dans une collection d'Animal.

C'est pour éviter ce genre de situations que certaines restrictions s'appliquent aux méthodes prenant en paramètre des collections paramétrées.

Peut-on assouplir ces règles ? Dans une certaine mesure, c'est possible. Par exemple en déclarant :

  1. public static void printAnimals(List<? extends Animal> animals)
  2. { ... }

Il est désormais possible d'appeler la méthode avec une List<Dog> (puisque Dog extends Animal). Dans la méthode, on pourra toujours parcourir la collection, mais pour éviter le problème évoqué plus haut, il sera alors interdit d'y ajouter de nouveaux éléments.

Par exemple, en modifiant ainsi le corps de la méthode :

  1. public static void printAnimals(List<? extends Animal> animals)
  2. {
  3. // Lecture OK
  4. for (Animal a : animals)
  5. { System.out.println(a);
  6. }
  7. // Ecriture : problème !
  8. animals.add(Dog());
  9. animals.add(Animal());
  10. }

Le compilateur indique qu'il est impossible d'ajouter de nouveaux éléments à la collection animals, même un Animal !

Le cas des tableaux

Le compilateur est moins strict avec les tableaux, et il est parfaitement possible de passer un Dog à une méthode réclamant un Animal. Mais le même problème de compatibilité des types demeure : une plus grande liberté à la compilation ouvre la porte à des erreurs de type runtime, plus difficiles à diagnostiquer, et provoquant le plantage de l'application :

  1. public static void printAnimals(Animal[] animals)
  2. {
  3. // Lecture : OK
  4. for (Animal a : animals)
  5. { System.out.println(a);
  6. }
  7. // Ajout d'un type différent : problème !
  8. animals[0] = new Cat();
  9. }

L'exécution de cet exemple provoque une erreur de type ArrayStoreException :

Exception in thread "main" java.lang.ArrayStoreException: Cat
	at Main.printAnimals(Main.java:30)
	at Main.main(Main.java:37)

Commentaires

1. Le mercredi 28 janvier 2009, 16:56 par José

Je vois trois problèmes !
- si la classe Main n'est pas publique, je ne pense pas que le code soit exécutable (mais je ne pense pas que ce soit l'objet du Quiz)
- passer un Dog pour un Animal fonctionne, sauf que rien ne nous empêche d'ajouter un Animal qui n'est pas un Dog au tableau passé en paramètre. Et là, le problème n'apparaîtra qu'à l'exécution. Mais à mon avis l'objet du Quiz est encore ailleurs.
- List<Dog> pour List<Animal> ? Hmmm... sans avoir le flair d'un Dog, je sens que l'objet du Quiz est plutôt là !

2. Le jeudi 29 janvier 2009, 13:05 par bhamp0

La méthode "public static void printAnimals(List<Animal> animals)" devrait être "public static void printAnimals(List<T extends Animal> animals)" si je ne m'abuse ... et ensuite, travailler avec le type T et non le type Animal dans le corps de la méthode.

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.