Au coeur du JDK : l'interface Iterable

Connaissez-vous l’interface java.lang.Iterable ?
Mais si, vous la connaissez : vous l’utilisez tous les jours, lorsque vous parcourez une Collection. Hé bien, voyons maintenant comment vous pouvez l’utiliser à votre avantage lorsque vous concevez un modèle objet.

Iterable, mode d’emploi

L’interface Iterable signale que la classe qui l’implémente est composée d’un ensemble de sous-éléments que le code appelant peut parcourir.
Elle déclare pour cela une unique méthode iterator(), devant renvoyer un Iterator sur l’ensemble des sous-éléments.

  1. public interface Iterable<T> {
  2. /**
  3.   * Returns an iterator over a set of elements of type T.
  4.   * @return an Iterator.
  5.   */
  6. Iterator<T> iterator();
  7. }

L’interface java.util.Collection étend Iterable, ce qui nous autorise à parcourir ses implémentations (notamment List et Set) de manière uniforme :

  1. List<String> words = Arrays.asList(« Hello », « World »);
  2. Iterator<String> it = words.iterator();
  3. while(it.hasNext()) {
  4. System.out.println(it.next());
  5. }

C’est également Iterable qui vous permet d’utiliser la boucle « foreach » apparue avec Java 5 (JLS $14.14.2).

EnhancedForStatement:
for ( VariableModifiersopt Type Identifier: Expression) Statement
The Expression must either have type Iterable or else it must be of an array type (§10.1), or a compile-time error occurs.

  1. List<String> words = Arrays.asList(« Hello », « World »);
  2. for (String word : words) {
  3. System.out.println(word);
  4. }

Implémenter Iterable dans votre code

Lorsque nous concevons un modèle objet, il arrive fréquemment que nous devions modéliser des relations de type « 1-N avec attributs ». Leur implémentation la plus évidente est une classe possédant possédant des champs correspondant aux attributs de la relation, ainsi qu’une collection encapsulant les sous-éléments.
Une partition musicale en est un bon exemple : elle est constituée d’une séquence de notes (les sous-éléments), ainsi que de divers attributs comme le nom du morceau.
En voici une implémentation naïve :

  1. public class Partition {
  2. private String nom;
  3. private List<Note> notes;
  4. public String getNom() {
  5. return nom;
  6. }
  7. public void setNom(String nom) {
  8. this.nom = nom;
  9. }
  10. public List<Note> getNotes() {
  11. return notes;
  12. }
  13. public void setNotes(List<Note> notes) {
  14. this.notes = notes;
  15. }
  16. }

Ce type d’implémentation est très répandu, mais malheureusement très mauvais du point de vue de la conception objet, car il rompt le principe d’encapsulation : l’accesseur de la liste
des notes permet de modifier celle-ci sans que la Partition ne soit avertie, et expose son implémentation interne.
Il faut donc trouver un moyen pour autoriser le développeur à parcourir la liste sans l’exposer directement.
C’est là qu’intervient l’interface Iterable.
Voici le code remanié :

  1. public class Partition implements Iterable<Note> {
  2. private final String nom;
  3. private final List<Note> notes = new ArrayList<Note>();
  4. public Partition(String nom, List<Note> notes) {
  5. this.nom = nom;
  6. if (notes != null) {
  7. this.notes.addAll(notes);
  8. }
  9. }
  10. public String getNom() {
  11. return nom;
  12. }
  13. public Iterator<Note> iterator() {
  14. return notes.iterator();
  15. }
  16. }

Ce remaniement amène un second bénéfice, pour l’utilisateur cette fois : il est beaucoup plus facile et naturel de parcourir la partition, note par note.

  1. Partition partition = new Partition(« Au clair de la lune », Arrays.asList(C, C, C, D, E, D, C, E, D, D, C));
  2. for(Note note : partition) {
  3. //Instrument.play(note);
  4. }

Conclusion

En conclusion, lorsque vous encapsulez une collection au sein d’une classe, pensez à l’interface Iterable ; votre code sera mieux encapsulé et plus facile à utiliser !

2 pensées sur “Au coeur du JDK : l'interface Iterable

  • 1 novembre 2010 à 19 h 05 min
    Permalink

    Merci pour cet article.

    J’avais voulu faire la même chose sur un projet, mais j’avais rencontré certains problèmes avec cette approche. J’ai commencé un commentaire, mais il est beaucoup trop long, je vais donc écrire un post en réponse (et me créer un blog du même coup 😉 ).

    Sinon, la classe Partition de votre example n’est pas immutable. En effet, il est possible de supprimer des notes en appelant remove() sur l’Iterator obtenu via partition.getIterator().

    Pour éviter ce problème, il faudrait utiliser un itérateur non modifiable, en créant un Iterator anonyme délégant à l’Iterator des notes, ou en utilisant un wrapper tel que l’UnmodifiableIterator de Guava (et sa factory-method: http://guava-libraries.googlecode.c… ) :

    import com.google.common.collect.Iterators;

    // …

    public Iterator<Note> iterator() {

       return Iterators.unmodifiableIterator(notes.iterator());

    }

    Répondre

Répondre à Etienne Neveu Annuler la réponse.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

%d blogueurs aiment cette page :