If you have an object that needs an iterator, and you happen to give it an infinite iterator, then all should be well. Of course, that object should never require the iterator to stop. And that is the potentially bad design.
Think of a music player. You tell it to start playing tracks in "continuous play mode" and it plays tracks until you tell it to stop. Maybe you implement this continuous play mode by shuffling a playlist forever... until the user presses "stop". In this case, MusicPlayer
needs to iterator over a playlist, that might be Collection<Track>
, forever. In this case, you'd want either a CyclicIterator<Track>
as you've built or a ShuffleIterator<Track>
that randomly picks the next track and never runs out. So far, so good.
MusicPlayer.play(Iterator<Track> playlist)
wouldn't care whether the play list ends or not. It works both with an iterator over a list of tracks (play to the end, then stop) and with a ShuffleIterator<Track>
over a list of tracks (pick a random track forever). All still good.
What if someone tried to write MusicPlayer.playContinuously()
--which expects to play tracks "forever" (until someone presses "stop"), but accepts an Iterator<Track>
as the parameter? That would be the design problem. Clearly, playContinuously()
needs an infinite Iterator
, and if it accepts a plain Iterator
as its parameter, then that would be bad design. It would, for example, need to check hasNext()
and handle the case where that returns false
, even though it should never happen. Bad.
So implement all the infinite iterators you want, as long as their clients don't depend on those iterators ending. As if your client depends on the iterator going on forever, then don't give it a plain Iterator
.
This should be obvious, but it doesn't seem to be.