Lamdas 绕过 try/catch 块来检查异常

dbl*_*dbl 5 java lambda exception java-8

由于我试图提取一些我在大多数项目中使用的常见包装 lambda 例程,我已经能够创建CheckedFunction,由PermeableFunctionFunctionalInterface子类化,绕过 try/catch 块的需要。我已经在用于 windows(v1.8.0_251)/linux(v1.8.0_261) 和其他几个在线编译器的 Oracle jdks 上测试了它(不确定那里使用了哪个实现)。

不确定这是否真的违反了规范或者是标准允许的......根据我对文档的解释,这应该是不可能的:

更准确地说,假设 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

CheckedFunction 还允许像这样对 throwable 进行阴影处理:

在此处输入图片说明

所以这是我的问题:

这是应该报告给实施者的事情还是标准强加的一般问题?

Hol*_*ger 6

你的方法

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

被我的 Eclipse 版本以及javac从 9 到 14 的所有 JDK拒绝。只有 JDK 8 接受它,所以这是一个错误,但不值得报告,因为较新的版本没有它。

也就是说,通过泛型类型系统颠覆异常检查是可能的。

当您将方法更改为

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

所有编译器都会接受它,但会产生“未经检查”的警告。这是众所周知的事情。

您可以将示例简化为:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

即使没有 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如前所述,这是众所周知的,要点是,您永远不应该忽略“未经检查”的警告。