为什么 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);
我有以下问题:
- 为什么我们需要使用 compose 调用进行转换而不是使用 andThen
来电?
- 为什么我们需要使用中间 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()
的函数可以是采用任何其他参数类型的函数。暂且讨论到此为止,编译器不支持这种推断。
我成功执行了以下表达式:
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);
我有以下问题:
- 为什么我们需要使用 compose 调用进行转换而不是使用 andThen 来电?
- 为什么我们需要使用中间 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()
的函数可以是采用任何其他参数类型的函数。暂且讨论到此为止,编译器不支持这种推断。