我正在学习Java 8中的Streams。
例如,如果我必须加倍数字:
Arrays.stream(intArray).map(e->e*2).forEach(System.out::println);
如果我必须对数字进行平方,那么我可以在下面使用:
Arrays.stream(intArray).map(e->e*e).forEach(System.out::println);
但是如果我必须使用"andThen"
方法java.util.function.Function
在同一个整数数组上应用这两个函数,我通过以下方式进行:
Function<Integer, Integer> times2 = e -> e * 2;
Function<Integer, Integer> squared = e -> e * e;
Arrays.stream(intArray).map(times2.andThen(squared)).forEach(System.out::println);
是否可以在单行中重写此(3个语句),如:
Arrays.stream(intArray).map(e->e*2.andThen(f->f*f)).forEach(System.out::println);
这给了我一个编译器错误。有可能吗?
答案 0 :(得分:4)
看起来Java并不隐含地假设lambda表达式是特定的Functional类型。我必须添加强制转换才能使它工作:
Arrays.stream( new int[]{ 1, 2, 3, 4 } )
.map( ( (IntUnaryOperator)( e -> e*2 ) ).andThen(f->f*f) )
.forEach(System.out::println);
我个人不喜欢这样,更愿意使用双地图电话。我很想知道一个更好的主意。
答案 1 :(得分:3)
如果您绝对必须使用.andThen
方法,那么此处的其他答案就是您要找的内容。但是,如果您正在寻找一种在单行流畅的流操作中组合两个函数的简单方法,那么只需调用.map
两次就是我能想到的最易读和最紧凑的形式:
Arrays.stream(intArray).map(e->e*2).map(f->f*f).forEach(System.out::println);
答案 2 :(得分:2)
@tsolakp的答案显示了如何通过将lambda表达式转换为所需的函数类型来创建特定类型的内联lambda。
至于为什么你不能这样做:
Arrays.stream(intArray)
.boxed()
.map(e -> e * 2.andThen(e -> e * e)) // wrong! does not compile!
.forEach(System.out::println);
原因是在Java中,lambdas和方法引用必须具有目标类型,因为它们不是该语言的第一公民。实际上,lambda必须是特定的SAM类型(单个抽象方法类型)。 SAM类型由功能接口表示,即仅具有一个抽象方法的接口。功能接口的示例包括Function
,Predicate
,Consumer
,Supplier
,Runnable
等,或者您自己声明的任何SAM类型,例如:< / p>
@FunctionalInterface
public interface Whatever<T, R> {
R thisIsMySingleAbstractMethod(T argument);
}
与Whatever
功能类型匹配的lambda表达式可以是:
Whatever<Integer, String> whatever = number -> number.toString();
但是同样的lambda表达式可以用于声明Function<Integer, String>
:
Function<Integer, String> whatever = number -> number.toString();
此示例显示lambdas在将其定位到SAM类型之前没有自己的类型。只有在这一点之后,该类型的方法才可用于链接。
在流中,map
方法需要Function
作为参数。如果您可以使用内联lambda表达式表达此类函数,则此lambda表达式将自动推断为map
方法的参数类型。这将发生(至少在概念上)在之后,编译器已经评估了lambda表达式。
但是,如果你想使用lambda表达式的目标类型的方法(在你的例子中这将是andThen
),那是不可能的,因为编译器还没有找到目标类型。
这就是为什么强制使用lambda表达式:它会提前告诉编译器 lambda表达式的目标类型是什么。