Java Reference
In-Depth Information
With that method in hand, we can define the other two methods in the iterator using default methods.
For
next()
, we call
maybeNext
and pass in
true
to advance. We get back an
Optional
result, and we use the
Optional API to either return its contained value or throw a
NoSuchElement
exception. For
hasNext()
, we call
maybeNext
and pass in
false
to avoid advancing. Then we simply let the caller know if the result is present.
All of this goes into our new interface, which extends
Iterator
. Now users can assign the lambda into this
type in order to get an Iterator out. This code, and a demonstration of how it works, is in Listing 7-12.
Listing 7-12.
Making Iterator More Lambda Friendly
import java.util.*;
import java.util.concurrent.atomic.*;
public class Listing12 {
public interface LambdaIterator<E> extends Iterator<E> {
Optional<E> maybeNext(boolean advance);
default E next() {
return maybeNext(true).orElseThrow(NoSuchElementException::new);
}
default boolean hasNext() {
return maybeNext(false).isPresent();
}
}
public static Iterator<String> generateIterator() {
String[] values = new String[]{"Hello", " ", "World"};
AtomicInteger nextI = new AtomicInteger(0);
LambdaIterator<String> it = advance -> {
if (nextI.get() >= values.length) return Optional.empty();
String result = values[nextI.get()];
if (advance) nextI.incrementAndGet();
return Optional.of(result);
};
return it;
}
public static void main(String[] args) {
Iterator<String> it = generateIterator();
}
}
The big trick with this approach is that you have to come up with the clever single method that can
handle all the needs of any given method. Your super-method does have access to the other methods in
the interface, but if you use those other methods, then you need to be extremely careful not to produce a
recursive loop. Coming up with a good, single method is the kind of problem that should excite you as a
developer.