【问题标题】:Side effects of lambda expression in java 8java 8 中 lambda 表达式的副作用
【发布时间】:2019-01-16 14:00:11
【问题描述】:

请考虑下面的代码 sn-p。

List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
List<String> copyList = new ArrayList<String>(); 
Consumer<String> consumer = s->copyList.add(s);
list.stream().forEach(consumer);

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

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

我的意思是,这是一个好的做法,会导致任何副作用吗?

【问题讨论】:

  • 最后几行可以重构为list.stream().forEach(copyList::add);
  • forEach 从来都不是纯粹的函数式,因为没有副作用它不会对流做任何事情。这适用于 Scheme 中的 for-each,JS 中的 Array.forEach,...

标签: java java-8 functional-programming


【解决方案1】:

forEach 如果不产生副作用,将毫无用处,因为它没有返回值。因此,每当您使用forEach 时,您都应该期待会发生副作用。因此,您的示例没有任何问题。

Consumer&lt;String&gt; 可以打印String,或将其插入某个数据库,或将其写入某个输出文件,或将其存储在某个Collection(如您的示例中)等...

来自StreamJavadoc:

流管道由源(可能是数组、集合、生成器函数、I/O 通道等)、零个或多个中间操作(将流转换为另一个流,例如 Stream .filter(Predicate))和一个终端操作(产生一个结果或副作用,例如 Stream.count() 或 Stream.forEach(Consumer)) .

此外,如果您查看 Consumer 的 Javadoc,您会发现它可能会产生副作用:

java.util.function.Consumer

表示接受单个输入参数并且不返回结果的操作。与大多数其他功能接口不同,消费者需要通过副作用进行操作

我猜这意味着 Java Streams 和函数式接口并非旨在仅用于“纯”函数式编程。

【讨论】:

  • 更一般地说,Consumer 的定义是产生副作用。
【解决方案2】:

对于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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-16
    • 1970-01-01
    • 2012-06-25
    • 1970-01-01
    • 2015-06-01
    • 2015-04-17
    • 1970-01-01
    • 2015-07-05
    相关资源
    最近更新 更多