I am trying to recursively find all inner exceptions (getCause's) from a top level exception...of a specific instance type.
public class MyCustomRunTimeException extends RuntimeException {
public MyCustomRunTimeException() {
}
public MyCustomRunTimeException(Exception innerException) {
super(innerException);
}
}
Here is what I've tried:
and my early "find" method:
private void findAllSpecificTypeOfInnerExceptions(Exception ex)
{
Collection<MyCustomRunTimeException> MyCustomRunTimeExceptions = Stream.iterate(ex, Throwable::getCause)
.filter(element ->
element != null
&& element instanceof MyCustomRunTimeException
)
.map(obj -> (MyCustomRunTimeException) obj)
.collect(Collectors.toList());
}
It is not working. :( I've tried several other things (not shown yet)... I'll post them as "appends" to this question if I get anything that doesn't throw an exception. Not working.... I'm getting NullPointers exceptions. and (depending on my tweaks) java.lang.reflect.InvocationTargetException exception.
Here is some examples that would find 1:N "matches".
Exception exampleOne = new MyCustomRunTimeException();
Exception exampleTwo = new Exception(new MyCustomRunTimeException());
Exception exampleThree =new Exception(new Exception(new MyCustomRunTimeException()));
Exception exampleFour =new Exception(new Exception(new MyCustomRunTimeException(new ArithmeticException())));
This is a case where I really think it's easiest doing the recursive bit in a loop, rather than using streams entirely:
List<Throwable> exlist = new ArrayList<>();
while(ex.getCause()!=null) {
exlist.add(ex.getCause());
ex = ex.getCause();
}
exlist = exlist.stream().filter(e -> e instanceof MyCustomRunTimeException).collect(Collectors.toList());
There are better options in Java 9+, and if you use external libraries, but not really in core Java 8 (hacks do exist, but that makes it much less clear than the above, IMHO, and I really don't see that it's worth introducing an external library just for this purpose.)
Or this variation:
private <T extends Throwable> Collection<T> aggregateSubClassExceptions(Throwable inputEx, Class<T> type) {
Collection<T> returnItems = new ArrayList<>();
Throwable exc = inputEx;
while (exc != null) {
if (type.isInstance(exc)) {
returnItems.add(type.cast(exc));
}
exc = exc.getCause();
}
return returnItems;
}
This code was getting the specific type AND any subclasses:
Collection<MyCustomRunTimeException> testItOutCollection;
Exception exampleOne = new MyCustomRunTimeException();
Exception exampleTwo = new Exception(new MyCustomRunTimeException());
Exception exampleThree = new Exception(new Exception(new MyCustomRunTimeException()));
Exception exampleFour = new Exception(new Exception(new MyCustomRunTimeException(new ArithmeticException())));
MyCustomRunTimeException exampleFive = new MyCustomRunTimeException(new MySubMyCustomRunTimeException(new MyCustomRunTimeException(new ArithmeticException())));
testItOutCollection = this.aggregateSubClassExceptions(exampleOne, MyCustomRunTimeException.class);
testItOutCollection = this.aggregateSubClassExceptions(exampleTwo, MyCustomRunTimeException.class);
testItOutCollection = this.aggregateSubClassExceptions(exampleThree, MyCustomRunTimeException.class);
testItOutCollection = this.aggregateSubClassExceptions(exampleFour, MyCustomRunTimeException.class);
testItOutCollection = this.aggregateSubClassExceptions(exampleFive, MyCustomRunTimeException.class);
The problem is that Stream.iterate
produces an infinite stream. It never knows when to stop, even if the cause is null.
You can pass a stopping condition in the form of takeWhile
but it is only available in Java 9+. See also: Limit a stream by a predicate for Java 8 workarounds (I recommend this)
Collection<MyCustomRunTimeException> exceptions = Stream.iterate(ex, Throwable::getCause)
.takeWhile(throwable -> throwable.getCause() != null)
.filter(element -> element instanceof MyCustomRunTimeException)
.map(obj -> (MyCustomRunTimeException) obj)
.collect(Collectors.toList());
You can still create your recursive solution, using java-8. Here is a small helper method that you need:
private static Stream<Throwable> streamIt(Throwable initial) {
if (initial == null) {
return Stream.empty();
}
return Stream.concat(
Stream.of(initial),
Stream.of(initial)
.map(Throwable::getCause)
.flatMap(StackWalkerSandbox::streamIt));
}
This will give you a Stream<Throwable>
, what you do next with it, it's entirely up to you. For example for your description that you wanted and taken into account the examples from the accepted answer, you could do:
streamIt(exampleFive)
.filter(MyCustomRunTimeException.class::isInstance)
.map(MyCustomRunTimeException.class::cast)
.collect(Collectors.toList())
.forEach(System.out::println);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With