Well, a more functional approach, in my opinion, will be to use the Try monad, which, unfortunately, is not for us in jdk 8 :(
However, you can still use the best monad library that provides it. With this, you can come up with some implementation as follows:
public static <Out> Try<Out> tryTimes(int times, TrySupplier<Out> attempt) { Supplier<Try<Out>> tryAttempt = () -> Try.ofFailable(attempt::get); return IntStream.range(1, times) .mapToObj(i -> tryAttempt) .reduce(tryAttempt, (acc, current) -> () -> acc.get().recoverWith(error -> current.get())) .get(); }
In short, this function simply calls tryAttempt calls, and if it fails, it attempts to recoverWith call the next tryAttempt call. The client code will look like this:
tryTimes(10, () -> { // all the logic to do your possibly failing stuff } );
As a result, the client code will receive Try<T> , which can be unpacked by a direct call to .get() (if successful, returns a value, in case of failure, generates the main exception) or with other methods described in the library documentation.
Hope this helps.
UPDATE:
This can also be done in a functional way, using filter , findFirst and limit and without any external libraries:
interface ThrowingSupplier<Out> { Out supply() throws Exception; } public static <Out> Optional<Out> tryTimes(int times, ThrowingSupplier<Out> attempt) { Supplier<Optional<Out>> catchingSupplier = () -> { try { return Optional.ofNullable(attempt.supply()); } catch (Exception e) { return Optional.empty(); } }; return Stream.iterate(catchingSupplier, i -> i) .limit(times) .map(Supplier::get) .filter(Optional::isPresent) .findFirst() .flatMap(Function.identity()); }
Client code remains unchanged. Also note that it will not evaluate the expression times times, but will stop on the first successful attempt.