java 绕过已检查异常的try/catch块的Lambda

qcbq4gxm  于 2022-12-17  发布在  Java
关注(0)|答案(1)|浏览(180)

由于我试图提取一些常见的 Package lambda例程(我在大多数项目中使用这些例程),我已经能够创建CheckedFunctionPermeableFunction FunctionalInterface的子类,它绕过了try/catch块的需要。我已经在Oracle jdks for windows上测试过了(v1.8.0_251)/linux(v1.8.0_261)和其他几个在线编译器(不确定那里使用的是哪个实现)。
不确定这是否真的违反了规范或者是标准允许的......根据我对文档的解释,这是不可能的:
更准确地说,假设B是一个类或接口,A是B的超类或超接口,B中的方法声明n覆盖或隐藏了A中的方法声明m,则:

  • 如果n有一个throws子句提到了任何检查过的异常类型,那么m必须有一个throws子句,否则会发生编译时错误。
  • 对于列在n的throws子句中的每一个被检查的异常类型,相同的异常类或其超类型之一必须出现在m的throws子句的擦除(§4.6)中;否则会发生编译时错误。
  • 如果m的未删除throws子句不包含n的throws子句中每个异常类型的超类型,则会出现编译时未检查警告。

下面是我使用的示例代码:

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还允许隐藏throwable,如下所示:

所以我的问题是:
这是应该向实现者报告的事情,还是标准强加的一般问题?

b4lqfgs4

b4lqfgs41#

你的方法

static <T, R, E extends Exception> CheckedFunction<T, R, E> from(WrappedFunction<T, R> wrappedFunction) {
    return wrappedFunction::applyChecked;
}

我的Eclipse版本以及从9到14的所有JDK的javac都拒绝了它。只有JDK 8接受它,所以这是一个bug,但不值得报告,因为较新的版本没有它。
也就是说,通过泛型类型系统破坏异常检查是可能的。
将方法更改为

static <T, R, E extends Exception> CheckedFunction<T, R, E> from(WrappedFunction<T, R> wrappedFunction) {
    return (CheckedFunction)(CheckedFunction<T, R, Exception>)wrappedFunction::applyChecked;
}

所有的编译器都会接受它,但是会产生一个“未检查”的警告。2这是已知的。
您可以将示例简化为:

public class Main {
    public static void main(String[] args) {
        CheckedFunction<Path, Long, RuntimeException> function = (CheckedFunction)
            (CheckedFunction<Path, Long, IOException>)Files::size;
        Path doesNotExist = Paths.get("/does/not/exist");

        function.applyChecked(doesNotExist); // throws NoSuchFileException without the need of a try/catch block!
    }

    interface CheckedFunction<T, R, E extends Exception> {
        R applyChecked(T t) throws E;
    }
}

即使没有lambda表达式,也有几种可能的变体。它所需要的只是一个使用类型参数的throws声明和一个关于该类型参数的未检查操作。
例如

public class Main {
    public static void main(String[] args) {
        try {
            Files.size(Paths.get("/does/not/exist"));
        }
        catch(IOException ex) {
            doThrow(ex); // throws undeclared IOException
        }
    }

    static <T extends Throwable> void doThrow(Throwable t) throws T {
        throw (T)t;
    }
}

正如所说,这是众所周知的,外卖是,你永远不应该忽视“未检查”的警告。

相关问题