为什么一个方法引用ctor“抛出”……抛出?
我正在寻找一种优雅的方式来创建一个dependency injection工厂。 在我的例子中,工厂只需调用一个参数构造函数。 我找到了这个答案,概述了如何将Function
用于此类目的。
但我的问题是:在我的情况下,我的ctor声明抛出一些检查exception。
我没有得到:使用对该构造函数的方法引用创建该函数不起作用。 如:
import java.util.function.Function; public class Mcve { public Mcve(String s) throws Exception { // whatever } public static void main(String[] args) { Function mcveFactory = Mcve::new; } }
告诉我关于Mcve::new
“Unhandled exception:java.lang.Exception”。 虽然这段代码没有调用构造函数。
两个问题:
- 为什么那个错误? 上面的代码没有调用ctor(还)?
- 有没有优雅的方法来解决这个难题? (简单地向我的
main()
添加throws Exception
没有帮助)
您需要提供一个自定义接口ThrowingFunction
,它有一个抛出Exception
方法。
public interface ThrowingFunction { ReturnType invoke(ParameterType p) throws Exception; } public class Mcve { public Mcve(String s) throws Exception { // whatever } public static void main(String[] args) { ThrowingFunction mcveFactory = Mcve::new; } }
使用这种方法导致调用mcveFactory.invoke("lalala");
强制你处理构造函数抛出的exception。
错误的原因是您要存储的实际函数引用(不是100%确定术语)会引发exception,因此类型根本不匹配。 如果你可以将Mcve::new
存储在一个函数中,那么调用该函数的人不再知道可以抛出exception。 如果实际抛出exception会怎么样? 抛出exception并丢弃它都不起作用。
替代方案:如果您需要实际检索Function
,那么您需要编写一个调用构造函数的函数(或lambda),捕获exception并将其丢弃或重新抛出包装在未经检查的RuntimeException
。
public class Mcve { public Mcve(String s) throws Exception { // whatever } public static void main(String[] args) { Function mcveFactory = parameter -> { try { return new Mcve(parameter); } catch (Exception e) { throw new RuntimeException(e); // or ignore } }; } }
我认为错误消息本身至少有点误导,因为你通常在实际调用方法时会看到它。 我当然可以理解导致第一个子问题的混乱。 更清楚(遗憾的是不可能)陈述类似的东西
不兼容的类型
Function
vs.Function
。throws Exception
我最近不得不这样做…如果你可以改变类定义,你可以使用臭名昭着的鬼鬼祟祟的做法:
static class OneArg { private final String some; @SuppressWarnings("unchecked") public 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 mcveFactory = OneArg::new;
我一直在思考这个问题,确实如果你想要一个能明确宣告你的意图的Function
,我认为你需要有一个扩展java.util.Function
的Function
,如下所示:
@FunctionalInterface public interface ThrowingFunction extends Function { 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
静默包装它的方法。