allgemein
objekt-orientierter entwurf
programmierung
qualitätssicherung / testen

bugTeaser  #001: Der unschuldige Iterator  
Schwierigkeitsgrad  einfach 
Lösung veröffentlicht am   07.02.2005  

Beobachtbare Symptome

Die Ausfühurng der select-Methode scheint – bis auf wenige Ausnahmen - die Programmausführung einzufrieren. Manchmal kommt es auch zu einer Ausnahme, weil anscheinend kein Speicher mehr verfügbar ist.

Erklärung

Die Collection-Schnittstelle im Java Collection-Framework deklariert die Methode iterator(). Diese liefert einen Iterator, mit dessen Hilfe über die Elemente der jeweiligen Collection-Instanz iteriert werden kann. Hier der entsprechende JavaDoc-Kommentar:

Returns an iterator over the elements in this collection. There are no guarantees concerning the order in which the elements are returned (unless this collection is an instance of some class that provides a guarantee).

Returns:
an Iterator over the elements in this collection

Hier ergibt sich ein erster – wenn auch feiner – Hinweis auf die Fehlerursache: "Returns an Iterator over the elements in this collection". Es wird ein Iterator zurück gegeben. Das lässt den Schluss zu, dass ein wiederholter Aufruf der iterator-Methode stets einen neuen oder zumindest einfach irgendeinen Iterator zurück gibt.

Der Programmierer unserers bugTeasers ist nun irrtümlicher Weise davon ausgegangen, dass eine Collection genau einen Iterator besitzt und auf diesen mittels der iterator-Methode zugegriffen werden kann. Dieses Missverständnis führt dazu, dass die gewählte Implementierung von select fehlerhaft ist, da durch die wiederholten Aufrufe der iterator-Methode auf der Collection jedesmal ein neuer Iterator zurück geliefert wird und die select-Methode für nicht leere Collections in eine Endlosschleife gerät. Läuft das Programm lange genug und liefert selector.select für das erste Element in der Collection true zurück, steigt der Speicherverbrauch kontinuierlich an, und es kommt irgendwann zu einem Speicherüberlauf.

Hier der idiomatische und korrekte Gebrauch des Iterators:

Iterator iter = items.iterator();
while (iter.hasNext()) {
  Object element = iter.next();
  if(selector.select(element)) {
    result.add(element); 
  }
}

Bleibt noch die Frage, warum der Programmierer überhaupt auf den Gedanken gekommen ist, dass eine Collection über nur einen Iterator verfügen soll. Wahrscheinlich ist, dass der Programmierer das entsprechende Entwufsmuster Iterator wie in Design Patterns beschrieben nicht kannte. Denn dort wird unter dem Punkt „Applicability„ (S.259) festgehalten:

"Use the pattern to support multiple traversals of aggregate objects."

Zudem wird auch das Problem der Iterator-Erzeugung thematisiert und gelöst: Die jeweilige Collection bietet eine Fabrikmethode createIterator, die eine neue Iterator-Instanz zurück gibt. Die iterator-Methode auf Collection ist in Java genau dies: eine Fabrikmethode, nur ist das klärende create aus dem Namen verschwunden und auch im Dokumentationskommentar findet sich kein expliziter Hinweis mehr, der auf diesen doch wichtigen Umstand hinweist. Für die Dokumentation auf einer Schnittstelle wiegt dieses Versäumnis schwer, denn sie erfüllt einen doppelten Zweck: sie informiert einerseits die Verwender der Schnittstelle über den korrekten Gebrauch und andererseits den Implementierer über die korrekte Umsetzung.

Gerade weil in Java in der Sprache verankert durch den Compiler nur die syntaktische nicht aber die semantische Korrektheit der Schnittstellenimplementierungen und ihrer Verwendung garantiert werden kann, ist eine möglichst präzise Dokumentation unerlässlich. Auch diese Lektion hält der vorliegende bugTeaser für uns bereit.

Über diesen bugTeaser diskutieren.

Druckbare Version