As a result of me trying to extract some common wrapping lambda routines that I use in most of my projects, I've been able to create CheckedFunction, subclassed by PermeableFunction FunctionalInterface that bypasses the need of try/catch blocks. I've tested that on Oracle jdks for windows(v1.8.0_251)/linux(v1.8.0_261) and several other online compilers(not sure which implementation is used there).
Wasn't sure if this actually violates the specification or is something allowed by the standard... According to my interpretaion of the docs this should not be possible:
More precisely, suppose that B is a class or interface, and A is a superclass or superinterface of B, and a method declaration n in B overrides or hides a method declaration m in A. Then:
- If n has a throws clause that mentions any checked exception types, then m must have a throws clause, or a compile-time error occurs.
- For every checked exception type listed in the throws clause of n, that same exception class or one of its supertypes must occur in the erasure (§4.6) of the throws clause of m; otherwise, a compile-time error occurs.
- If the unerased throws clause of m does not contain a supertype of each exception type in the throws clause of n, a compile-time unchecked warning occurs.
Here is the sample code that I've used:
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
PermeableFunction<Path, Long> function = PermeableFunction.from(Files::size);
Path doesNotExist = Paths.get("/does/not/exist");
// function.apply(doesNotExist); -> throws WrappedException
function.applyChecked(doesNotExist); // throws NoSuchFileException without the need of a try/catch block!
}
}
interface PermeableFunction<T,R> extends CheckedFunction<T, R, RuntimeException> {
static <T, R> PermeableFunction<T, R> from(WrappedFunction<T, R> wrappedFunction) {
return CheckedFunction.<T,R, RuntimeException>from(wrappedFunction)::applyChecked;
}
}
interface CheckedFunction<T, R, E extends Exception> extends WrappedFunction<T, R> {
@Override
R applyChecked(T t) throws E;
static <T, R, E extends Exception> CheckedFunction<T, R, E> from(WrappedFunction<T, R> wrappedFunction) {
return wrappedFunction::applyChecked;
}
}
interface WrappedFunction<T, R> extends Function<T, R> {
R applyChecked(T t) throws Exception;
@Override
default R apply(T t) {
try {
return applyChecked(t);
} catch (Exception e) {
throw new WrappedException(e);
}
}
}
class WrappedException extends RuntimeException {
public WrappedException(Throwable cause) {
super(cause);
}
}
CheckedFunction also allows shadowing of the throwable like so:
So here is my question:
Is this something that should be reported to the implementer(s) or it's a general issue imposed by the standard?
