Strong typing

Failsafe APIs are typed based on the expected execution result. For generic policies that are used for various executions, the result type may just be Object:

RetryPolicy<Object> retryPolicy = RetryPolicy.ofDefaults();

But for other policies you might declare a more specific result type:

RetryPolicy<HttpResponse> retryPolicy = RetryPolicy.<HttpResponse>builder()
  .handleResultIf(response -> response.getStatusCode() == 500)
  .onFailedAttempt(e -> log.warn("Failed attempt: {}", e.getLastResult().getStatusCode()))

This allows Failsafe to ensure that the same result type used for the policy is returned by the execution and available in event listeners:

HttpResponse response = Failsafe.with(retryPolicy)
  .onSuccess(e ->"Success: {}", e.getResult().getStatusCode()))  

It also ensures that when multiple policies are composed, they all share the same result type:

CircuitBreaker<HttpResponse> circuitBreaker = CircuitBreaker.ofDefaults();
Failsafe.with(retryPolicy, circuitBreaker).get(this::connect);