【问题标题】:Java 8 functional constructor from templated object来自模板化对象的 Java 8 函数式构造函数
【发布时间】:2015-10-06 21:06:15
【问题描述】:

我正在使用 Eclipse Luna Service Release 2 (4.4.2),Java 8 u51。

我正在尝试创建一个方法,该方法将基于另一个方法参数创建传递对象的实例。原型被简化为

public <T> T test(Object param, T instance) {
    Constructor<?> constructor = instance.getClass().getConstructors()[0]; // I actually choose a proper constructor

    // eclipse reports "Unhandled exception type InvocationTargetException"
    Function<Object, Object> createFun = constructor::newInstance;

    T result = (T) createFun.apply(param);
    return result;
}

在线与Function声明eclipse报Unhandled exception type InvocationTargetException编译错误。我需要 Function 稍后在流中使用。

我尝试添加各种 try/catch 块,抛出声明,但没有解决此编译器错误。

如何使这段代码工作?

【问题讨论】:

  • 您认为Function 绕道与简单地在Constructor 上调用newInstance 相比有什么优势?
  • @Holger 我在Constructor 流中使用newInstance,它产生了相同的消息,所以我创建了一个这样的SSCE。函数detour其实是需要保持一个相对可读的stream()链的。
  • 我明白了。因此,关于用例和 assylias 建议的 lambda 表达式应该可以工作,所以它有点过于简单化了。但是我现在想知道的是,对于结果类型是否真的是 instance 的确切类型(可能是 T 的子类),是否真的有硬性要求,换句话说,是否诉诸反射真的很有必要……
  • @Holger 我找不到无反射的解决方案。该方法确实用于 T 的许多子类。我想要一个定义清晰但可能很难看但实现单一的方法。我最终传递了一个 Class&lt;T&gt; 而不是 T 的实例。
  • 嗯,使用Class 排除了结果是该类的子类的可能性。如果你在调用者的站点上有一个已知的具体类型,那么像Function&lt;A,B&gt; f=B::new; 这样的构造应该是可能的。如果调用者本身是通用的,它就不会那么容易工作,但它必须从某个地方获取Class 对象,不是吗?也许有一个无反射的解决方案,但这似乎值得提出另一个问题(有关用例的更多信息)......

标签: java eclipse generics functional-programming java-8


【解决方案1】:

这是因为方法引用是指向带有在编译时错误中命名的异常的方法。单一抽象方法R apply(T t ) 在java.util.Function 不会抛出这些异常。所以 java.util.Function 的函数类型和你的 Method-Reference 的类型不匹配。

使用具有匹配方法的功能类型有效...例如:

    interface MyFunction<T,R> {
        /**
         * Applies this function to the given argument.
         *
         * @param t the function argument
         * @return the function result
         */
        R apply(T t) throws  Throwable;

    }

    public <T> T test(Object param, T instance) throws InstantiationException, IllegalAccessException,
           IllegalArgumentException, InvocationTargetException {
        Constructor<?> constructor = instance.getClass().getConstructors()[0]; // I actually choose a proper constructor

        // eclipse reports "Unhandled exception type InvocationTargetException"
            MyFunction<Object,Object> createFun = constructor::newInstance;

        T result = (T) createFun.apply(param);
        return result;
    }

【讨论】:

    【解决方案2】:

    您不能从具有 Function 目标类型的 lambda 中抛出已检查异常,因为它的 apply 方法不会抛出异常。所以你需要把它变成一个未经检查的异常,例如通过包装它:

    Function<Object, Object> createFun = o -> {
      try {
        return constructor.newInstance(o);
      } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
        throw new RuntimeException(e);
      }
    };
    

    另一种方法是让编译器认为这是一个未经检查的异常,与上面的选项相比,它会产生更清晰的堆栈跟踪:

    Function<Object, Object> createFun = o -> {
      try {
        return constructor.newInstance(o);
      } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) {
        return uncheck(e);
      }
    };
    

    使用以下实用方法:

    @SuppressWarnings("unchecked")
    public static <E extends Throwable, T> T uncheck(Throwable t) throws E {
      throw ((E) t);
    }
    

    【讨论】:

    • 你可以从 lambda 抛出一个检查异常。但不是来自应该实现 Function 的 lambda,因为 Function.apply() 不会抛出已检查的异常。
    • 您可以通过引入&lt;T extends Throwable&gt; 绑定来收紧您的uncheck 方法,因此您不必进行任何类型的转换。你也可以在 uncheck 方法中执行你的 throw;当你可以扔一个时,不需要两种方法。
    猜你喜欢
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 2016-06-24
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    相关资源
    最近更新 更多