Schaltet dies den Vertrag von Iterator
?
Nr
Die Java Iterator
erlegt zwei "Verträge". Der erste Vertrag ist die Java-Schnittstelle selbst, die 3 Methoden deklariert: hasNext()
, next()
und remove()
. Jede Klasse, die diese Schnittstelle Iterator
implementiert, muss diese Methoden definieren.
Der zweite Auftrag definiert das Verhalten des Iterator
:
hasNext()
[...] liefert true, wenn die Iteration mehr Elemente hat. [...] next()
liefert das nächste Element in der Iteration [und] löst NoSuchElementException
aus, wenn die Iteration keine Elemente mehr enthält.
Das ist der gesamte Vertrag.
Es ist wahr, dass, wenn der zugrunde liegende XMLStreamReader
fortgeschritten ist, kann es Ihre BoxIterator
und/oder DrawerIterator
durcheinander bringen. Alternativ könnte das Aufrufen der BoxIterator.next()
und/oder DrawerIterator.next()
an den falschen Stellen die Iteration durcheinander bringen. Jedoch korrekt verwendet, wie in Ihrem Beispielcode oben, funktioniert es richtig und vereinfacht den Code erheblich. Sie müssen nur die ordnungsgemäße Verwendung der Iteratoren dokumentieren.
Als ein konkretes Beispiel implementiert die Scanner
Klasse Iterator<String>
und hat noch viele, viele andere Methoden, die den zugrunde liegenden Stream voranbringen. Wenn es einen stärkeren Vertrag von der Klasse Iterator
gegeben hätte, dann würde die Klasse Scanner
selbst dies verletzen.
Als Ivan weist darauf hin, in den Kommentaren, sollte boxList
nicht von class BoxIterator implements Iterator<Box>, Iterable<Box>
Typ sein. Sie sollten wirklich haben:
class BoxList implements Iterable<Box> { ... }
class BoxIterator implements Iterator<Box> { ... }
BoxList boxList = ...;
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId()
}
}
Während mit einer Klasse implementieren beide Iterable
und Iterator
ist technisch nicht falsch für Ihren Anwendungsfall, es zu Verwechslungen führen kann.
Betrachten Sie diesen Code in einem anderen Zusammenhang:
List<Box> boxList = Arrays.asList(box1, box2, box3, box4);
for(Box box : boxList) {
// Do something
}
for(Box box : boxList) {
// Do some more stuff
}
Hier wird boxList.iterator()
zweimal aufgerufen, zwei separate Iterator<Box>
Instanzen zu erstellen, für zweimal die Liste der Boxen laufen. Da die boxList
mehrfach durchlaufen werden kann, erfordert jede Iteration eine neue Iteratorinstanz.
In Ihrem Code:
BoxIterator boxList = new BoxIterator(xml_stream);
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId();
}
}
, weil Sie über einen Stream iterieren, können Sie nicht (ohne den Strom Zurückspulen oder die extrahierten Objekte zu speichern) ein zweites Mal über den gleichen Knoten durchlaufen. Eine zweite Klasse/Objekt wird nicht benötigt; Das gleiche Objekt kann sowohl als Iterable als auch als Iterator fungieren ... was Ihnen eine Klasse/Objekt erspart.
Having said that, ist vorzeitige Optimierung die Wurzel allen Übels. Die Ersparnisse einer Klasse/eines Objekts sind die mögliche Verwirrung nicht wert; Sie sollten BoxIterator
in eine BoxList implements Iterable<Box>
und BoxIterator implements Iterator<Box>
teilen.
Ihre Beschreibung sieht aus wie 'Box.iterator' ein neues' DrawerIterator' zurückgibt und wenn das so der Vertrag gebrochen, da das wird nicht der Fall ist 'DrawerIterator' sollte sowieso nur Elemente innerhalb der aktuellen Box zurückgeben. – Thomas
@Thomas 'Box.iterator()' gibt bei jedem Aufruf den gleichen 'DrawerIterator' zurück, da alle auf den gleichen zugrundeliegenden Stream zugreifen. Dies bedeutet, dass selbst ein 'DrawerIterator', der durch einen früheren Aufruf von' Box.iterator() 'zurückgegeben wird, magisch weiterentwickelt wird. Alle greifen immer auf den zugrunde liegenden Stream an derselben Cursorposition zu. – Roland
Ah ich sehe. Das würde dann den Vertrag brechen. Müssen Sie bei jedem Anruf dieselbe Instanz zurückgeben? Wenn Sie jedes Mal eine neue Instanz zurückgeben und sequentiell iterieren würden (d. H. Kein zufälliger Zugriff), wäre es egal, ob die Cursorposition vorgerückt wäre. Nachdem Sie über eine Box iteriert haben, "fädeln Sie jeden weiteren Aufruf dieser Box" ein. DrawerIterator 'hasNext()' sollte false zurückgeben. – Thomas