【问题标题】:Can someone explain to me this method signature有人可以向我解释这个方法签名吗
【发布时间】:2020-07-05 05:54:28
【问题描述】:

我正在查看以下代码(其他人的代码),部分代码对我来说似乎晦涩难懂:

List<string> result = forkJoinPool.invokeAll(tasks)
            .stream()
            .map(MUtil.rethrowFunction(Future::get))
            .collect(toList());

这部分很简单,一个 ForkJoinPool.invokeAll() 返回一个 Future 对象列表,进一步处理返回一个字符串列表。

稍后,list.stream().map() 使用 Mutil 类 Mutil.rethrowFunction(Future::get) 上的静态方法并将其传递给 Future 类型的对象。查看 Mutil 源代码:

public class Mutil {
    public static <T, R> Function<T, R> rethrowFunction(WithExceptionsIF<T, R> function) {
        return t -> {
            try { return function.apply(t); }
            catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }
    @FunctionalInterface
      public interface WithExceptionsIF<T, R> {
          R apply(T t) throws Exception;
    }

    @SuppressWarnings ("unchecked")
    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
}

这是我的问题,因为我正在学习泛型、lambda 和 java.util.function 包:
  1. Mutil.rethrowFunction() 的签名表示它返回&lt;T, R&gt; Function&lt;T, R&gt; 并使用WithExceptionsIF&lt;T, R&gt; 类型的参数(这是一个功能接口)。 Future.get() 如何转换成Mutil.rethroFunction() 签名? Future.get(),返回计算结果而不是函数?

  2. 传递给Mutil.rethrowFunction()Future.get()如何转换为WithExceptionIF&lt;T,R&gt;类型?

  3. t-&gt;{return function.apply(t);}这句话是怎么回事?不是 Future 对象,如果是,那么“函数”是谁?

  4. Mutil.throwAsUnchecked(Exception exception) 方法的签名在关键字“static”之后定义了&lt;E extends Throwable&gt;。如果“异常”是传递给方法的唯一参数,那么 E 来自哪里,为什么要在方法的返回类型(void)之前声明它?

感谢您的澄清。

【问题讨论】:

    标签: java generics lambda


    【解决方案1】:

    这是一个完整的可运行示例。

    package org.example;
    
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.Future;
    import java.util.function.Function;
    
    import static java.util.stream.Collectors.toList;
    
    public class SO62737345 {
        public static void main(String... args) {
            ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
            List<Callable<String>> tasks = Collections.singletonList(() -> "fish");
            List<String> result = forkJoinPool.invokeAll(tasks)
                    .stream()
                    .map(rethrowFunction(Future::get))
                    .collect(toList());
        }
    
        public static <T, R> Function<T, R> rethrowFunction(WithExceptionsIF<T, R> function) {
            return t -> {
                try {
                    return function.apply(t);
                }
                catch (Exception exception) { throwAsUnchecked(exception);  return null; }
            };
        }
        @FunctionalInterface
        public interface WithExceptionsIF<T, R> {
            R apply(T t) throws Exception;
        }
    
        @SuppressWarnings ("unchecked")
        private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E { throw (E)exception; }
    }
    
    1. get() 方法在调用 rethrowFunction 时不会执行。对rethrowFunction 的调用只是将传入的函数从引发检查异常的函数转换为不引发检查异常的函数(因为任何异常都包含在未经检查的异常中)。 collect() 方法实际上调用了该函数,为 Stream 中的每个 Future 调用一次。

    2. 考虑使用 lambda 而不是方法引用来传递 Future::get。应该是f -&gt; f.get()。因为我们传递了一个成员函数,所以它将它所属的对象作为参数。所以我们传递了一个函数,因为 get() 的签名是V get() throws InterruptedException, ExecutionException,它匹配WithExceptionsIF。事实上,在这种特殊情况下,它是WithExceptionsIF&lt;Future,String&gt; 的一个实例。

    3. 如果你通过替换上面的 lambda 来扩展它,你会得到: return (f -&gt; f.get()).apply(t) 所以get is being applied to the parameter being passed when the function which rethrowFunction`返回被评估。

    4. 这里解释了A peculiar feature of exception type inference in Java 8 Java 正在执行“类型推断”。编译器被告知@​​987654335@ 抛出了一个对象,该对象的类是 Throwable 的子类。因为rethrowFunction 的返回类型没有声明检查异常,所以编译器决定throwAsUnchecked 必须抛出RuntimeException。另一种看待它的方式是,在这种情况下,类型参数的值是从调用函数的上下文中推断出来的,而不是从它的参数中推断出来的。至少我觉得是这样的,不是那么容易理解的!

    更一般地解释通用语法,通常方法是参数化类型的一部分,例如List&lt;A&gt;,所以类型参数可以出现在成员函数的签名中,例如A get(int i)。您还可以拥有一个泛型函数,其签名具有未出现在类中的类型参数(或与类的实例无关的静态函数),并且在这里它们在返回类型之前声明。

    【讨论】:

    • 谢谢 tgdavies,这澄清了我的问题的第 2 部分和第 3 部分,但是,第 1 部分是关于返回类型的,这仍然是一个谜。一旦 Future.get() 执行,返回将是一个计算值,它如何转换为声明 Function 作为返回类型的 Mutil.rethrowFunction() 签名?如果 Future.get() 返回一个 Integer,让我们来看看,它如何与 函数 匹配?再次感谢
    • 我已经更新了我的答案,让我知道是否更清楚
    • 非常感谢您;您在 (1) 中添加的评论现在非常清楚。我更好地理解了代码,并且我可以遵循逻辑,但是,我只是遇到了另一个障碍,这是最后一个,我保证:-)。我将在第 4 部分更新我的帖子,如果您也可以对此有所了解。
    • 恕我直言,您的回答中有几处不正确。我在回答中引用了其中一些。
    • 我已将完整代码添加到我的答案中。在Future::get 上放置一个断点,你会看到它被collect() 调用,而在rethrowFunction() 返回很久之后。我将澄清“那个”是指rethrowFunction()。所有成员函数都隐式地将它们所属的对象作为参数。
    【解决方案2】:

    get() 方法在调用 rethrowFunction 时不会执行...“

    这是不正确的。 Future::getrethrowFunction()s 调用链中被调用过;作为调用 rethrowFunction() 的副作用。 Future::get 的返回类型是 MUtil.WithExceptionsIF。它是作为输入参数传递给 rethrowFunction() 的返回值。

    这只是将传入的函数从一个引发检查异常的函数转换为一个不会......

    这句话的措辞令人困惑。它读起来好像句子中的“That”指的是Future::getget() 方法不这样做 transformrethrowFunction() 方法是最繁重的工作。

    考虑传递 Future::get...因为我们传递的是一个成员函数,它将它所属的对象作为参数

    Future.get() doesn't take any parameters.

    ...因为 get() 的签名是 V get() throws InterruptedException, ExecutionException 在这种特殊情况下匹配 WithExceptionsIF...“

    这也不对。 Future.get() 的签名与任何事情无关。 Future.get() 的签名肯定不匹配WithExceptionsIF。同样,在此特定调用中,Future.get()返回类型 将是 WithExceptionsIF。但是方法的返回类型is not part of its signature

    Future.get() 转换为 *WithExceptionsIF&lt;T,R&gt;,因为 forkJoinPool.invokeAll(tasks) 返回一个 List&lt;Future &lt;MUtil.WithExceptionsIF&lt;T,R&gt;&gt;&gt;

    如果你通过替换上面的 lambda 来扩展它,你会得到:return (f -&gt; f.get()).apply(t) 所以get is being applied to the parameter being passed when the function which rethrowFunction` 的返回值会被计算。

    不正确。同样,Future.get() 与此无关。 Future.get()s 工作在将 WithExceptionsIF 传递到 rethrowFunction 后完成。 t -&gt; {...} 块定义了一个 lambda,它由对 rethrowFunction() 的调用返回。 t 是 lambda 的输入参数。它的类型将是类型参数 T 被实例化为的任何类型。

    WithExceptionsIF.apply() 方法的一些具体实现是实际调用该 lambda 时所调用的;在以后某个未知的时间点。

    我们看到的function有一个WithExceptionsIF&lt;T, R&gt;的实例传入rethrowFunction(WithExceptionsIF&lt;T, R&gt; function)

    ...如果“异常”是传递给方法的唯一参数,那么 E 来自哪里,为什么要在方法的返回类型(void)之前声明它?

    这个:static &lt;E extends Throwable&gt; void throwAsUnchecked(Exception exception)how generic methods are declared。在类型参数部分声明的 E 类型变量用于方法的 throws 子句。也在它的身体里;将传入该方法的 Exception 转换为另一个更具体的类型,该类型将在实际调用泛型方法时指定。

    最后但同样重要的是:List&lt;string&gt;... 分配错误有几个原因。主要原因是您示例中的流不产生Stream&lt;String&gt;。它产生 Stream&lt;Function&lt;T, R&gt;&gt;

    【讨论】:

      猜你喜欢
      • 2020-11-27
      • 1970-01-01
      • 1970-01-01
      • 2011-01-16
      • 2010-12-17
      • 1970-01-01
      • 2011-07-14
      • 2014-10-13
      • 2010-10-04
      相关资源
      最近更新 更多