public final boolean hasNext() {
        if (state == State.FAILED) throw new IllegalStateException();

        switch (state) {
            case DONE:
                return false;
            case GOT_NEXT:
                return true;
            default:
        }

        // handle tryNext
        state = State.FAILED; // just in case
        next = tryNext();

        if (state != State.DONE) {
            state = State.GOT_NEXT;
            return true;
        }
        return false;
    }