为什么 compose 方法需要强制转换,而 andThen 方法不需要

Why casting is required with compose method but not with andThen Method

我成功执行了以下表达式:

Function<Long,Long> y = ((Function<Long,Long>)(x -> x*x)).andThen(x -> x+1).andThen(x -> x+2);

我明白为什么这里的第一个 lambda 表达式需要转换。但是下面的 lambda 给出了错误,即“x+1”不是第二个 compose lambda 表达式的有效操作

Function<Long,Long> y = ((Function<Long,Long>)(x -> x*x)).compose(x -> x+1).compose(x -> x+2);

我能够使用带有 compose 的转换解决上述错误:

Function<Long,Long> y = ((Function<Long,Long>)(x -> x*x)).compose((Function<Long,Long>)x -> x+1).compose(x -> x+2);

我有以下问题:

  1. 为什么我们需要使用 compose 调用进行转换而不是使用 andThen 来电?
  2. 为什么我们需要使用中间 compose 调用进行转换而不是使用 终端撰写电话?

原因是 Function.compose and Function.andThen 的行为不相同且不可交换。

如果你运行下面的代码。

    Function<Long,Long> y1 = ((Function<Long,Long>)(x -> x*x)).andThen(x -> x+1).andThen(x -> x+2);

    System.out.println(y1.apply(10l));

    Function<Long,Long> y2 = ((Function<Long,Long>)(x -> x*x)).compose((Long x) -> x+1).compose(x -> x+2);

    System.out.println(y2.apply(10l));

即使我们 运行 两个函数具有相同的值 (10),它 returns 不同的值。使用 andThen 的地方是 returns 103 (10x10+(1+2)),使用 compose 的地方是 returns 169 (10+1+2, 13x13)。因此 compose 在乘法 lambda 应用之前被调用并且 compose 得到一个 Function<Object, Long> 作为参数而不是 Function<Long, Long> compose 对之前发生的任何 lambda 没有可见性,因为它会第一个被调用。

由于调用 compose 时没有上下文,我们需要转换为 Function<Long, Long> 或像我所做的那样在 lambda 本身中使用类型。希望这有帮助。

Why do we need casting with compose calls but not with andThen calls?

这两种方法是不同的。 compose() 接受一个函数,其输入的类型不一定与当前函数的参数类型相同。这是一个稍微修改过的示例,表明编译器不必假设 Long:

Function<Long, Long> f = (x -> x * x);
Function<String, Long> g = f.compose(Long::parseLong);

您可以观察到 f.compose() 有一个 String 类型的参数。在上面的代码中,它是从赋值上下文中推断出来的(即,编译器知道输入是 String 类型的,因为结果函数被分配给了一个 Function<String, Long> 变量)。

然而,当涉及到 .andThen() 时,对于编译器来说事情就更简单了:类型参数 <V> 用于给定函数的输出(而不是输入,就像这种情况一样)对于 compose)。并且因为它已经知道输入类型,所以它有所有的信息:.andThen(x -> x+1)只能有Long作为输出类型,因为Long + int会产生long,装箱到Long。结束.

Why do we need casting with intermediate compose calls but not with terminal compose calls?

现在想想,如果我这样写会怎么样?

Function<String, Long> g = f.compose(Long::parseLong).compose(Long::parseLong);

发生的事情是,由于赋值上下文,编译器已准备好将 last .compose()<V> 推断为 String (看上面)。
问题是:它是否应该假设中间 .compose()String?答案是肯定的在这种情况下*(因为Long.parseLong只需要一个字符串,没有重载),但编译器不会这样做;这是 known limitation.

我可以让它与 f.<String>compose(Long::parseLong).compose(Long::parseLong); 一起工作(这当然打破了我的最后一个 .compose() 调用,原因很明显,但你明白了。

换句话说,你可以用

修复它

类型证人

...<Long>compose(x -> x + 1).compose(x -> x + 2)

显式参数类型(我的首选)

    ...compose((Long x) -> x + 1).compose(x -> x + 2)

*我说“是的在这种情况下”因为你不能指望编译器总是知道类型。它在这里是明确的,因为带有单个参数的 Long.parseLong 没有被重载,所以我们可以争辩说编译器可以将中间 .compose()<V> 推断为 <String>。但这不应理解为编译器应该能够在所有情况下执行此类推理。传递给 .compose() 的函数可以是采用任何其他参数类型的函数。暂且讨论到此为止,编译器不支持这种推断。