请考虑以下代码段。
AttributeValue
由于我们使用的是lambda表达式,因此按照函数式编程(纯函数),它应该只计算输入并提供相应的输出。
但是在此示例中,它试图将元素添加到既不在lambda范围内也未声明的列表中。
我的意思是,这是一种好习惯吗,会导致任何副作用?
答案 0 :(得分:6)
forEach
如果不产生副作用,将没有用,因为它没有返回值。因此,每当您使用forEach
时,您都应该期待发生副作用。因此,您的示例没有错。
Consumer<String>
可以打印String
,或将其插入某些数据库,或将其写入某些输出文件,或将其存储在某些Collection
中(如您的示例),等等...
通过Stream
Javadoc:
流管道由源(可能是数组,集合,生成器函数,I / O通道等),零个或多个中间操作(将一个流转换为另一个流,例如Stream)组成.filter(Predicate))和终端操作(产生结果或产生副作用,例如Stream.count()或Stream.forEach(Consumer))
此外,如果您查看Consumer
的Javadoc,将会发现它具有副作用:
java.util.function.Consumer
表示一个接受单个输入参数且不返回结果的操作。与大多数其他功能界面不同,消费者应通过副作用来操作。
我想这意味着Java Stream
和功能接口并非旨在仅用于“纯”功能编程。
答案 1 :(得分:1)
对于forEach
甚至stream().forEach
来说,它非常简单。 您的示例效果很好。
请注意,如果您可以使用其他流式传输方法执行此操作,那么您可能会得到一些惊喜: 以下代码绝对不会打印任何内容。
List<String> lst = Arrays.asList("a", "b", "c");
lst.stream().map(
s -> {
System.out.println(s);
return "-";
});
在这种情况下,流的行为更像构建者,该构建者准备流程但尚未执行。只有在调用collect
,count
或find...
方法时,lambda才会被执行。
发现这一点的一种简单方法是查看map
方法的返回类型,该方法又是Stream
。
话虽如此,我认为对于您的特定示例,还有更简单的选择。
List<String> list = Arrays.asList("A", "B", "C");
// this is the base pattern for transforming one list to another.
// the map applies a transformation.
List<String> copyList1 = list.stream().map(e -> e).collect(Collectors.toList());
// if it's a 1-to-1 mapping, then you don't really need the map.
List<String> copyList2 = list.stream().collect(Collectors.toList());
// in essence, you could of course just clone the list without streaming.
List<String> copyList3 = new ArrayList<>(list);