Java 8中Lambda表达式的副作用

时间:2018-08-09 07:39:11

标签: java java-8 functional-programming

请考虑以下代码段。

AttributeValue

由于我们使用的是lambda表达式,因此按照函数式编程(纯函数),它应该只计算输入并提供相应的输出。

但是在此示例中,它试图将元素添加到既不在lambda范围内也未声明的列表中。

我的意思是,这是一种好习惯吗,会导致任何副作用?

2 个答案:

答案 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 "-";
 });

在这种情况下,流的行为更像构建者,该构建者准备流程但尚未执行。只有在调用collectcountfind...方法时,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);