I often use the do-while-checkNextForNull-getNext loop template (I donโt know if there is an official name for it) in some of my projects. But in Java8, using an option is considered cleaner code than checking for null references in client code. But when you use the options in this loop pattern, the code becomes a bit verbose and ugly, but since the option has several convenient methods, I would expect a cleaner way than the one I came up with below.
Example:
Given the following class.
class Item { int nr; Item(nr) { this.nr = nr;
The first element always has nr == 1, and each element defines the next element, and you do not want to create unnecessary new elements.
In client code, I can use the following do-while-checkNextForNull-getNext loop:
Item item = new Item(1); do { // do something with the item .... } while ((item = item.next()) != null);
With Java8 optional, this class will look like this:
class Item { .... Optional<Item> next() { return ...someCondition.... ? Optional.of(new Item(nr + 1)) : Optional.empty(); } }
And then the while-checkNextForNull-getNext loop pattern gets a little ugly and verbose:
Item item = new Item(1); do { // do something with the item .... } while ((item = item.next().orElse(null)) != null);
The orElse(null)) != null feels uncomfortable.
I searched for other types of loops but couldn't find a better one. Is there a cleaner solution?
Update:
You can use the for-each loop while avoiding null references (using null references is considered bad practice). This solution was proposed by Xavier Delamotte and does not need Java8-optional.
Implementation with a common iterator:
public class Item implements Iterable<Item>, Iterator<Item> { int nr; Item(int nr) { this.nr = nr; // an expensive operation } public Item next() { return new Item(nr + 1); } public boolean hasNext() { return ....someCondition.....; } @Override public Iterator<Item> iterator() { return new CustomIterator(this); } }
and
class CustomIterator<T extends Iterator<T>> implements Iterator<T> { T currentItem; boolean nextCalled; public CustomIterator(T firstItem) { this.currentItem = firstItem; } @Override public boolean hasNext() { return currentItem.hasNext(); } @Override public T next() { if (! nextCalled) { nextCalled = true; return currentItem; } else { currentItem = currentItem.next(); return currentItem; } } }
Then the client code becomes very simple / clean:
for (Item item : new Item(1)) {
Although this can be considered a violation of the Iterator contract, because the new Item(1) object is included in the loop, whereas usually the for loop immediately calls next () and thereby skips the first object. In other words: for the first object, next () is violated because it returns the first object.