代码之家  ›  专栏  ›  技术社区  ›  GhostCat

为什么方法引用“抛出”的ctor。。。还扔吗?

  •  19
  • GhostCat  · 技术社区  · 7 年前

    我正在寻找一种优雅的方法来创建依赖注入工厂。在我的例子中,工厂只需要调用一个单参数构造函数。我找到了这个 answer 概述如何使用 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” Mcve::new . 虽然此代码是 调用构造函数。

    两个问题:

    • 为什么会出现这种错误?以上代码不适用 调用ctor(尚未)?
    • 有什么优雅的方法可以解决这个难题吗?(只需添加 throws Exception 给我的 main() 帮助)
    3 回复  |  直到 7 年前
        1
  •  24
  •   luk2302    7 年前

    您需要提供一个自定义接口 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"); 强制您处理构造函数引发的异常。

    如果 你可以储存 Mcve::new 在一个函数内,那么调用该函数的人不再 知道 例外 可以抛出。如果实际抛出异常,会发生什么?抛出异常和丢弃异常都不起作用。


    备选方案:如果您需要实际检索 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> vs。 Function<String,Mcve> throws Exception .

        2
  •  2
  •   Eugene    7 年前

    我最近不得不这么做。。。如果你 可以 更改类定义,您可以使用臭名昭著的鬼鬼祟祟的抛出方式进行操作:

    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;
    
        3
  •  1
  •   Eugene    7 年前

    我已经思考了一段时间,如果你想 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 .