I'm trying to clean up some Java code. There are many static factory methods that all do the same exception handling. As an example, consider createA
:
public static A createA() throws XXXX, YYYY {
try {
return somethingThatThrows();
} catch (InterruptedException | ExecutionException e) {
Throwable throwable = e.getCause();
if (throwable instanceOf XXXX) {
throw (XXXX) throwable;
} else if (e instance of YYYY) {
throw (YYYY) throwable;
} else if (throwable != null) {
throw new RuntimeException(throwable);
} else {
throw new RuntimeException(e);
}
}
}
There are many of these create
methods (each of which returns a different type). For each of these methods, a copy of this exception handling exists (i.e. it's duplicated). I'm hoping there is a way to avoid all of this identical code and only have this logic in one place.
Of course, without exception handling, you simply extract the logic to a helper function and the duplication is solved - the fact that this has exception handling makes it different. The following code does not build:
public static void helper(final Exception e) {
Throwable throwable = e.getCause();
if (throwable instanceOf XXXX) {
throw (XXXX) throwable;
} else if (e instance of YYYY) {
throw (YYYY) throwable;
} else if (throwable != null) {
throw new RuntimeException(throwable);
} else {
throw new RuntimeException(e);
}
}
public static A createA() throws XXXX, YYYY {
try {
return somethingThatThrows();
} catch (InterruptedException | ExecutionException e) {
handle(e);
}
}
Does anyone have any suggestions?
This can be handled in a functional way as below:
@FunctionalInterface
interface SomethingThatThrows<T> {
T execute() throws XXXX, YYYY, InterruptedException,ExecutionException;
}
private static <T> T handledFuntion(SomethingThatThrows<T> function) throws XXXX, YYYY {
try {
return function.execute();
} catch (InterruptedException | ExecutionException e) {
Throwable throwable = e.getCause();
if (throwable instanceof XXXX) {
throw (XXXX) throwable;
} else if (e instanceof YYYY) {
throw (YYYY) throwable;
} else if (throwable != null) {
throw new RuntimeException(throwable);
} else {
throw new RuntimeException(e);
}
}
}
// Use lambda literal - may be better when arguments are involved
public A createA(String arg1) throws XXXX, YYYY {
return handledFuntion(() -> {
// write code just like you'd write it in try{} body -
// all arguments to createA() are available
return new A(arg1);
});
}
// use a method handle, works best when there are no arguments
public B createB() throws XXXX, YYYY {
return handledFuntion(this::somethingThatMakesB);
}
private B somethingOtherThatMakesB() throws XXXX, YYYY, InterruptedException,ExecutionException {
// Some logic that creates and returns B
}
Edit: Incorporated @Arkadiy's answer.