Okay, the title is maybe hard to understand. I didn't find something correct.
So, basically I'm using Java 8 functions to create a Retryable API. I wanted an easy implementation of these interfaces, so I created an of(...) method in each implementation of the Retryable interface where we can use lambda expressions, instead of creating manually an anonymous class.
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public interface Retryable<T, R> extends Function<T, R>{
void retrying(Exception e);
void skipping(Exception e);
int trials();
@Override
default R apply(T t) {
int trial = 0;
while (true) {
trial++;
try {
return action(t);
} catch (Exception e) {
if (trial < trials()) {
retrying(e);
} else {
skipping(e);
return null;
}
}
}
}
R action(T input) throws Exception;
interface RunnableRetryable extends Retryable<Void, Void> {
static RunnableRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedRunnable runnable) {
return new RunnableRetryable() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public Void action(Void v) throws Exception {
runnable.tryRun();
return null;
}
};
}
@FunctionalInterface
interface CheckedRunnable extends Runnable {
void tryRun() throws Exception;
@Override
default void run() {
try {
tryRun();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface ConsumerRetryable<T> extends Retryable<T, Void> {
static <T> ConsumerRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedConsumer<T> consumer) {
return new ConsumerRetryable<T>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public Void action(T t) throws Exception {
consumer.tryAccept(t);
return null;
}
};
}
@FunctionalInterface
interface CheckedConsumer<T> extends Consumer<T> {
void tryAccept(T t) throws Exception;
@Override
default void accept(T t) {
try {
tryAccept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface SupplierRetryable<T> extends Retryable<Void, T> {
static <T> SupplierRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedSupplier<T> supplier) {
return new SupplierRetryable<T>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public T action(Void v) throws Exception {
return supplier.tryGet();
}
};
}
@FunctionalInterface
interface CheckedSupplier<T> extends Supplier<T> {
T tryGet() throws Exception;
@Override
default T get() {
try {
return tryGet();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
interface FunctionRetryable<T, R> extends Retryable<T, R> {
static <T, R> FunctionRetryable of(Consumer<Exception> retrying, Consumer<Exception> skipping, int trials, CheckedFunction<T, R> function) {
return new FunctionRetryable<T, R>() {
@Override
public void retrying(Exception e) {
retrying.accept(e);
}
@Override
public void skipping(Exception e) {
skipping.accept(e);
}
@Override
public int trials() {
return trials;
}
@Override
public R action(T t) throws Exception {
return function.tryApply(t);
}
};
}
@FunctionalInterface
interface CheckedFunction<T, R> extends Function<T, R> {
R tryApply(T t) throws Exception;
@Override
default R apply(T t) {
try {
return tryApply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
But as you can see, there's a lot of duplicate code in every of(...) methods. I could create a kind of "constructor" (that's not the correct word, because interfaces can't have a constructor) in the Retryable interface, but I don't know how. Does someone have an idea ?