lambda表达式与实例化方法引用之间的不同行为

st.*_*err 7 java lambda java-stream method-reference

据我所知,lambda表达式可以被方法引用替换而没有任何问题.我的IDE说的相同,但下面的例子显示了相反的情况.方法引用清楚地返回相同的对象,其中lambda表达式每次都返回新对象.

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Instance {

    int member;

    Instance set(int value){
        this.member = value;
        return this;
    }

    @Override
    public String toString() {
        return member + "";
    }

    public static void main(String[] args) {

        Stream<Integer> stream1 = Stream.of(1, 2, 3, 4);
        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4);

        List<Instance> collect1 = stream1.map(i -> new Instance().set(i)).collect(Collectors.toList());
        List<Instance> collect2 = stream2.map(new Instance()::set).collect(Collectors.toList());

        System.out.println(collect1);
        System.out.println(collect2);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的输出:

[1, 2, 3, 4]
[4, 4, 4, 4]
Run Code Online (Sandbox Code Playgroud)

ern*_*t_k 6

你的lambda表达式new Instance()每次执行时都会调用它.这解释了为什么toString()每个元素的结果都不同.

方法引用保留引用它的实例,使其类似于:

Instance instance = new Instance();
List<Instance> collect2 = stream2.map(instance::set).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

在这种情况下使用方法引用的结果是使用相同的实例来调用set,最后收集.member显示的值是最后一个.


作为一个实验,进行这些更改并观察实例在lambda表达式的情况下是否正在发生变化:

/* a random string assigned per instance */
private String uid = UUID.randomUUID().toString();

Instance set(int value) {
    this.member = value;
    System.out.println("uid: " + uid); //print the ID
    return this;
}
Run Code Online (Sandbox Code Playgroud)


dav*_*xxx 6

方法参考表达式评估的时间不同于lambda表达式中的哪一个.
使用具有::在子表达式之前的表达式(而不是类型)的方法引用,立即计算并且然后存储并重用评估结果.
所以在这里 :

new Instance()::set
Run Code Online (Sandbox Code Playgroud)

new Instance() 被评估一次.

15.12.4开始.方法调用的运行时评估(重点是我的):

方法参考表达式评估的时间比lambda表达式(第15.27.4节)更复杂.当方法引用表达式在:: separator之前具有表达式(而不是类型)时,将立即计算该子表达式.存储评估结果,直到调用相应功能接口类型的方法为止; 此时,结果将用作调用的目标引用.这意味着:: separator之前的表达式仅在程序遇到方法引用表达式时计算,并且不会在函数接口类型的后续调用中重新计算.