为什么一个方法引用“抛出”的ctor...也扔?

2022-09-02 12:56:26

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

但我的问题是:在我的情况下,我的ctor声明抛出一些检查的异常。

我没有得到的是:使用对该构造函数的方法引用创建该函数不起作用。如:

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;
    }
}

告诉我关于 “未处理的异常: java.lang.Exception” for .尽管此代码调用构造函数。Mcve::new

两个问题:

  • 为什么会出现这个错误?上面的代码没有调用 ctor(还没有)?
  • 有什么优雅的方法来解决这个难题吗?(简单地添加到我无济于事throws Exceptionmain()

答案 1

您需要提供一个自定义接口,该接口具有一个 throw .ThrowingFunctionException

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::newException


替代方案:如果你最终需要实际检索一个,那么你需要编写一个函数(或lambda),它调用构造函数,捕获异常并丢弃它或将其重新抛出包装在未选中的.Function<String, Mcve>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
            }
        };
    }
}

我认为错误消息本身至少有点误导,因为您通常在实际调用该方法时会看到它。我当然可以理解导致第一个子问题的混乱。更清楚(可惜是不可能的)陈述这样的事情

不兼容的类型与 .Function<String,Mcve>Function<String,Mcve> throws Exception


答案 2

我最近不得不这样做...如果您可以更改类定义,则可以使用臭名昭着的偷偷摸摸的投掷方式来做事:

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;