Below is a pseudo-code of what I want to achieve, in mostly complete/usable java.
An exception is thrown, and I want to check it against a list of known exception types without having to do a long series of if-checks to see if it is instanceof each type
//pretend this is the exception that was thrown
Exception e = new CustomException2();
//I want to have a set of expected types
Set<Type> expectedExceptionTypes = new HashSet<>(Arrays.asList(CustomException1, CustomException2, CustomException3));
//I want to see which type the exception matches to or else give a default (UnknownException)
Optional<Type> type = expectedExceptionTypes.stream().filter((t) -> e instanceof t).findFirst().orElse(UnknownException);
//... perform some action based on the exception type
How can I actually implement something like this? Am I on the right track here?
What I'm trying to avoid:
if(e instanceof CustomException1)
...
if(e instanceof CustomException2)
...
if(e instanceof CustomException3)
...
.
.
.
CodePudding user response:
There is a pattern called Chain of Responsibility that can be used to achieve what you want. It can have different forms, you're on the right track. What you need is change expectedExceptionTypes to hold the condition you're checking against and also the code to handle it. For example, define a class that packs those two together:
class ExceptionHandler {
Predicate<Exception> condition;
Consumer<Exception> handlingCode;
public ExceptionHandler(Predicate<Exception> condition, Consumer<Exception> handlingCode) {
this.condition = condition;
this.handlingCode = handlingCode;
}
}
Then is just finding the first handler that satisfies the condition, and call it:
final Set<ExceptionHandler> handlers = new HashSet<>(
Arrays.asList(
new ExceptionHandler(
v -> v instanceof CustomException1,
t -> System.out.println("Handling exception of type: " t)
),
new ExceptionHandler(
v -> v instanceof CustomException2,
t -> System.out.println("Handling exception of type: " t
)))
);
handlers.stream().filter((ExceptionHandler t) -> t.condition.test(e))
.findFirst()
.ifPresent(exceptionHandler -> exceptionHandler.handlingCode.accept(e));
Note I explicitly use instanceof inline, to avoid the type erasure problems, but any method that works can be used.
CodePudding user response:
Store the classes in the Set and use contains(e.getClass()).
Define your target classes (probably as static final constant):
Set<Class<?>> exceptionClasses = Set.of(CustomException1.class, CustomException2.class, CustomException3class); // since Java 9
Then given
Exception e = someException;
e = exceptionClasses.contains(e.getClass()) ? e : new UnknownException();
CodePudding user response:
You can bit modify your code by using Class instead of Type and comparing by Objects.equals(t.getCanonicalName(), e.getClass().getCanonicalName())(or e.getClass().isAssignableFrom(t) explanation below):
Exception e = new CustomException2();
Set<Class> expectedExceptionTypes = new HashSet<>(Arrays
.asList(CustomException1.class, CustomException2.class, , CustomException3.class));
// get type the exception matches to or else have a default UnknownException.class
Class type = expectedExceptionTypes
.stream()
.filter( t -> Objects.equals(t.getCanonicalName(), e.getClass().getCanonicalName()))
.findFirst()
.orElse(UnknownException.class);
//... perform some action based on the exception type
Use e.getClass().isAssignableFrom(t) instead of Objects.equals(t.getCanonicalName(), e.getClass().getCanonicalName()) if you don't want "exact match" (for more info Checking a class type (.class) is equal to some other class type)
