I have encountered that I must catch all checked exceptions inside stream expresions. I have read very popular topic:
How can I throw CHECKED exceptions from inside Java 8 streams?
And there is answer which suugest following approach:
we have 3 strings:
"java.lang.Object"
"java.lang.Integer"
"java.lang.Strin"
We want to load classes by name.
Thus we need to use Class#forName
method
Class#forName java doc)
As you can see following method declaration contains
throws ClassNotFoundException
Thus if we use usual loop we need to write followig code:
public void foo() throws ClassNotFoundException {
String arr[] = new String[]{"java.lang.Object", "java.lang.Integer", "java.lang.Strin"};
for(String className:arr){
Class.forName(className);
}
}
lets try to rewrite it using streams:
public void foo() throws ClassNotFoundException {
String arr[] = new String[]{"java.lang.Object", "java.lang.Integer", "java.lang.Strin"};
Stream.of(arr).forEach(Class::forName);
}
compiler says that:
Error:(46, 32) java: incompatible thrown types java.lang.ClassNotFoundException in method reference
Ok, lets try to use approach from topic mentioned above:
we create following methods:
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
throw (E) exception;
}
And client code will looks like this:
public void foo() throws ClassNotFoundException {
String arr[] = new String[]{"java.lang.Object", "java.lang.Integer", "java.lang.Strin"};
Stream.of(arr).forEach(rethrowConsumer(Class::forName));
}
I want to know how does it work. It is really unclear for me.
lets research rethrowConsumer:
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
and throwAsUnchecked signature look slike
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
Please clarify this mess.
P.S.
I localized that magic happens inside throwAsUnchecked
because following snippet correct
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
return t -> {
try {
throw new Exception();
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
But following not
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
throw new Exception();
//throwAsUnchecked(exception);
}
};
}
The Java compiler understands that
Class.forName()
throws a checked exception. And that needs to somehow be handled. But when catching that checked exception and making it look like it gets wrapped into aRuntimeException
for example - you resolve the compilation problem. By taking away the "thing" that the compiler complains about.Please note: the compiler does not understand that invoking
throwAsUnchecked(exception)
will always throw up. In other words: by making a method call here, there is actually no code inside that method body that must throw. Of course this doesn't change the runtime situation. In the end, there is still an exception thrown - and surprisingly not a RuntimeException, but an instance of ClassNotFoundException.The core point is that
<E extends Throwable>
translates into a RuntimeException (for the compiler). And that part is explained here!And because of that, the second version of
foo()
can be written like:there is no more need to declare throws ClassNotFoundException!
In other words: this approach works by "tricking" the compiler into thinking that the exception that gets thrown is an unchecked one. And that trick itself is rooted in generics/type-erasure.