Is there any way of handling -- and continuing from -- an exception in an iterator while maintaining the foreach syntactic sugar?
There is no such sugar.
Occasionally lines will be syntactically bogus, but that doesn't necessarily mean that we shouldn't keep reading the file.
Well, if it's not that exceptional the lines are bogus, why throw exceptions? You could rework your iterator a bit. Assuming you currently iterate over ParsedThingy
instances, and that the parser throws ThingyParseException
if parsing fails, iterate over wrappers which let you query the parse results for bogusness, like this:
for (Possibly<ParsedThingy, ThingyParseException> p : parser) {
if (p.exception() != null) handleException(p.exception());
else doSomethingExcitingWith(p.value());
}
Somewhat more self-documenting than seemingly spontaneously returning null
s; it also lets you give information about the error to the client code.
Possibly<V, X>
is a wrapper around a value which may in fact be an exception. You can query for an exceptional status by checking if exception()
is non-null, and get the value for the non-exceptional case by calling value()
(which will throw if it is an exception):
class Possibly<V, X extends Throwable> {
private final V value;
private final X exception;
public static <V, X extends Throwable> Possibly<V, X> forValue(V v){
return new Possibly<V, X>(v, null);
}
public static <V, X extends Throwable> Possibly<V, X> forException(X x){
if (x == null) throw new NullPointerException();
return new Possibly<V, X>(null, x);
}
private Possibly(V v, X x){ value = v; exception = x; }
public X exception(){ return exception; }
public V value() throws X {
if (exception != null) throw exception;
return value;
}
}
Then your iterator()
will look something like this:
Iterator<Possibly<ParsedThingy, ThingyParseException>> parse() {
return new Iterator<Possibly<ParsedThingy, ThingyParseException>> {
public boolean hasNext(){ ... }
public void remove(){ ... }
public Possibly<ParsedThingy, ThingyParseException> next()
try {
ParsedThingy t = parseNext(); // throws ThingyParseException
return Possibly.forValue(t);
} catch (ThingyParseException e) {
return Possibly.forException(e);
}
}
};
}
Kind of verbose, could be avoided by making stuff less generic.