为什么方法引用了 "throws" ... 也抛出的 ctor?

Why does a method reference to ctor that "throws" ... throw as well?

我正在寻找一种优雅的方式来创建依赖注入工厂。在我的例子中,工厂只需调用一个单参数构造函数。我发现此 概述了如何将 Function<ParamType, ClassToNew> 用于此类目的。

但我的问题是:在我的例子中,我的 ctor 声明抛出一些已检查的异常。

我不明白的是:使用对该构造函数的方法引用创建 Function 不起作用。如:

import java.util.function.Function;

public class Mcve {        
    public Mcve(String s) throws Exception {
        // whatever
    }        
    public static void main(String[] args) {
        Function<String, Mcve> mcveFactory = Mcve::new;
    }
}

告诉我关于 Mcve::new 的 "Unhandled exception: java.lang.Exception"。虽然这段代码不是调用构造函数。

两个问题:

您需要提供一个自定义接口 ThrowingFunction,它有一个抛出 Exception.

的方法
public interface ThrowingFunction<ParameterType, ReturnType> {
    ReturnType invoke(ParameterType p) throws Exception;
}

public class Mcve {
    public Mcve(String s) throws Exception {
        // whatever
    }
    public static void main(String[] args) {
        ThrowingFunction<String, Mcve> mcveFactory = Mcve::new;
    }
}

使用此方法会导致调用 mcveFactory.invoke("lalala"); 强制您处理构造函数抛出的异常。

错误的原因是您要存储的实际函数引用(不是 100% 确定术语)引发异常,因此类型根本不匹配。 如果 你可以将 Mcve::new 存储在一个函数中,那么调用该函数的人将不再 知道 并且可以抛出 Exception。如果真的抛出异常会发生什么?抛出异常和丢弃异常都不行。


替代方案:如果您最终需要实际检索 Function<String, Mcve>,那么您需要编写一个调用构造函数的函数(或 lambda),捕获异常并丢弃它或将其包裹在内部重新抛出未经检查的 RuntimeException.

public class Mcve {
    public Mcve(String s) throws Exception {
        // whatever
    }

    public static void main(String[] args) {
        Function<String, Mcve> mcveFactory = parameter -> {
            try {
                return new Mcve(parameter);
            } catch (Exception e) {
                throw new RuntimeException(e); // or ignore
            }
        };
    }
}

我认为错误消息本身至少有点误导,因为您通常在实际调用该方法时会看到它。我当然可以理解导致第一个子问题的混乱。像

这样的陈述会更清楚(遗憾的是不可能)

Incompatible types Function<String,Mcve> vs. Function<String,Mcve> throws Exception.

我最近不得不这样做...如果您 可以 更改 class 定义,您可以使用臭名昭著的偷偷摸摸的 throws 做事方式:

static class OneArg {

    private final String some;

    @SuppressWarnings("unchecked")
    public <E extends Exception> OneArg(String some) throws E {
        try {
            this.some = some;
            // something that might throw an Exception... 
        } catch (Exception e) {
            throw (E) e;
        }
    }

    public String getSome() {
        return some;
    }
}

Function<String, OneArg> mcveFactory = OneArg::new;

我已经考虑了一段时间,确实 - 如果你想要一个 Function 来清楚地声明你的意图,我认为你需要一个 Function扩展 java.util.Function,像这样:

@FunctionalInterface
public interface ThrowingFunction<T, R> extends Function<T, R> {

    R applyWithExc(T t) throws Exception;

    @Override
    default R apply(T t) {
        try {
            return applyWithExc(t);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

顺便说一句,您可以选择在定义构造函数引用时调用的方法 - 一个会抛出 Exception 的方法,另一个会用 RuntimeException 默默地包装它。