I'm frequently using the do-while-checkNextForNull-getNext looping pattern (don't know if there is an official name for it) in some of my projects. But in Java8, the use of Optional is considered as cleaner code than checking for null references in client-code. But when using Optional in this looping pattern, the code gets a bit verbose and ugly, but because Optional has some handy methodS, I would expect that there must exist 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;
// an expensive operation
}
Item next() {
return ...someCondition....
? new Item(nr + 1)
: null;
}
}
In which the first item always has nr==1 and each item determines the next item, and you don't want to create unnecessary new items.
I can use the following looping do-while-checkNextForNull-getNext pattern in client-code:
Item item = new Item(1);
do {
// do something with the item ....
} while ((item = item.next()) != null);
With Java8-Optional, the given class becomes:
class Item {
....
Optional<Item> next() {
return ...someCondition....
? Optional.of(new Item(nr + 1))
: Optional.empty();
}
}
And then the do-while-checkNextForNull-getNext looping pattern becomes a bit 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
part feels uncomfortable.
I have looked for other kind of loops, but haven't found a better one. Is there a cleaner solution?
Update:
It is possible to use a for-each loop while at the same time avoiding null-references (the use of null-references is considered as a bad practice). This solution has been proposed by Xavier Delamotte, and doesn't need Java8-Optional.
Implementation with a generic 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 client code becomes very simple/clean:
for (Item item : new Item(1)) {
// do something with the item ....
}
Although this may be seen as a violation of the Iterator contract because the new Item(1)
object is included in the loop, whereas normally, the for loop would immediately call next() and thus skipping the first object. In other words: for the first object, next() is violated because it returnS the first object itself.
See Question&Answers more detail:
os