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

时间:2017-11-22 12:29:35

标签: java constructor method-reference

我正在寻找一种优雅的方法来创建一个依赖注入工厂。在我的例子中,工厂只需要调用一个参数构造函数。我发现此answer概述了如何将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的“未处理的异常:java.lang.Exception”。虽然此代码调用构造函数。

两个问题:

  • 为什么会出错?上面的代码调用ctor(还)?
  • 有什么优雅的方法来解决这个难题吗? (只需将throws Exception添加到我的main() 帮助

3 个答案:

答案 0 :(得分:23)

您需要提供一个自定义界面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
            }
        };
    }
}

我认为错误消息本身至少有点误导,因为你在实际调用方法时通常会看到它。我当然可以理解导致第一个子问题的混乱。更明确(遗憾的是不可能)陈述类似

的内容
  

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

答案 1 :(得分: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;

答案 2 :(得分:1)

我一直在思考这个问题,确实如果你想要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静默包装它的方法。