实现原理

lambda 原理

以下面代码为例

1
2
3
4
5
6
7
8
9
public class TestLambda {
public static void main(String[] args) {
test((a, b) -> a + b);
}

static void test(BinaryOperator<Integer> lambda) {
System.out.println(lambda.apply(1, 2));
}
}

执行结果

1
3

第一步,生成静态方法

如何证明?用反射

1
2
3
for (Method method : TestLambda.class.getDeclaredMethods()) {
System.out.println(method);
}

输出为(去掉了包名,容易阅读)

1
2
3
public static void TestLambda.main(java.lang.String[])
static void TestLambda.test(BinaryOperator)
private static java.lang.Integer TestLambda.lambda$main$0(Integer,Integer)
  • 可以看到除了我们自己写的 main 和 test 以外,多出一个名为 lambda$main$0 的方法

  • 这个方法是在编译期间由编译器生成的方法,是 synthetic(合成)方法

  • 它的参数、内容就是 lambda 表达式提供的参数和内容,如下面代码片段所示

    1
    2
    3
    private static Integer lambda$main$0(Integer a, Integer b) {
    return a + b;
    }

第二步,生成实现类字节码

如果是我自己造一个对象包含此方法,可以这么做

先创建一个类

1
2
3
4
5
6
7
final class LambdaObject implements BinaryOperator<Integer> {

@Override
public Integer apply(Integer a, Integer b) {
return TestLambda.lambda$main$0(a, b);
}
}

将来使用时,创建对象

1
test(new LambdaObject());

只不过,jvm 是在运行期间造出的这个类以及对象而已,要想查看这个类

在 jdk 21 中运行时添加虚拟机参数

1
-Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles

早期 jdk 添加的参数是(没有去进行版本比对了)

1
-Djdk.internal.lambda.dumpProxyClasses

若想实现在运行期间生成上述 class 字节码,有两种手段

  • 一是动态代理,jdk 并没有采用这种办法来生成 Lambda 类
  • 二是用 LambdaMetaFactory,它配合 MethodHandle API 在执行时更具性能优势
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class TestLambda1 {
public static void main(String[] args) throws Throwable {
test((a, b) -> a + b);

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType factoryType = MethodType.methodType(BinaryOperator.class);
MethodType interfaceMethodType = MethodType.methodType(Object.class, Object.class, Object.class);
MethodType implementsMethodType = MethodType.methodType(Integer.class, Integer.class, Integer.class);

MethodHandle implementsMethod = lookup.findStatic(TestLambda1.class, "lambda$main$1", implementsMethodType);

MethodType lambdaType = MethodType.methodType(Integer.class, Integer.class, Integer.class);
CallSite callSite = LambdaMetafactory.metafactory(lookup,
"apply", factoryType, interfaceMethodType,
implementsMethod,
lambdaType);

BinaryOperator<Integer> lambda = (BinaryOperator) callSite.getTarget().invoke();
test(lambda);
}

static Integer lambda$main$1(Integer a, Integer b) {
return a + b;
}

static void test(BinaryOperator<Integer> lambda) {
System.out.println(lambda.apply(1, 2));
}
}

其中

  • “apply” 是接口方法名

  • factoryType 是工厂方法长相

  • interfaceMethodType 是接口方法长相

  • implementsMethod 是实现方法

    • implementsMethodType 是实现方法长相
  • lambdaType 是实际函数对象长相

  • callSite.getTarget() 实际是调用实现类的构造方法对应的 mh,最后 invoke 返回函数对象

方法引用原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TestLambda3 {
public static void main(String[] args) throws Throwable {
test(String::toLowerCase);

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType factoryType = MethodType.methodType(Function.class);
MethodType interfaceMethodType = MethodType.methodType(Object.class, Object.class);
MethodHandle implementsMethod = lookup.findVirtual(String.class, "toLowerCase", MethodType.methodType(String.class));
MethodType lambdaType = MethodType.methodType(String.class, String.class);
CallSite callSite = LambdaMetafactory.metafactory(lookup,
"apply", factoryType, interfaceMethodType,
implementsMethod,
lambdaType);

Function<String, String> lambda = (Function<String, String>) callSite.getTarget().invoke();
System.out.println(lambda.apply("Tom"));
}

static void test(Function<String,String> lambda) {
System.out.println(lambda.apply("Tom"));
}
}

闭包原理

捕获基本类型变量

1
2
3
4
5
6
int c = 10;
test((a, b) -> a + b + c);

static void test(BinaryOperator<Integer> lambda) {
System.out.println(lambda.apply(1, 2));
}

生成一个带 3 个参数的方法,但它和 BinaryOperator 还差一个 int 参数

1
2
3
static Integer lambda$main$1(int c, Integer a, Integer b) {
return a + b + c;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TestLambda2 {
public static void main(String[] args) throws Throwable {
// int c = 10;
// test((a, b) -> a + b + c);

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType factoryType = MethodType.methodType(BinaryOperator.class, int.class);
MethodType interfaceMethodType = MethodType.methodType(Object.class, Object.class, Object.class);
MethodType implementsMethodType = MethodType.methodType(Integer.class, int.class, Integer.class, Integer.class);

MethodHandle implementsMethod = lookup.findStatic(TestLambda2.class, "lambda$main$1", implementsMethodType);

MethodType lambdaType = MethodType.methodType(Integer.class, Integer.class, Integer.class);
CallSite callSite = LambdaMetafactory.metafactory(lookup,
"apply", factoryType, interfaceMethodType,
implementsMethod,
lambdaType);

BinaryOperator<Integer> lambda = (BinaryOperator) callSite.getTarget().invoke(10);
test(lambda);
}

static Integer lambda$main$1(int c, Integer a, Integer b) {
return a + b + c;
}

static void test(BinaryOperator<Integer> lambda) {
System.out.println(lambda.apply(1, 2));
}
}

不同之处

  • factoryType,除了原本的接口类型之外,多了实现方法第一个参数的类型
  • 产生 lambda 对象的时候,通过 invoke 把这个参数的实际值传进去

这样产生的 LambdaType 就是这样,并且生成 Lambda 对象时,c 的值被固定为 10

1
2
3
4
5
6
7
8
9
10
11
final class LambdaType implements BinaryOperator {
private final int c;

private TestLambda2$$Lambda(int c) {
this.c = c;
}

public Object apply(Object a, Object b) {
return TestLambda2.lambda$main$1(this.c, (Integer)a, (Integer)b);
}
}

捕获引用类型变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class TestLambda4 {
static class MyRef {
int age;

public MyRef(int age) {
this.age = age;
}
}
public static void main(String[] args) throws Throwable {
/*MyRef ref = new MyRef(10);
test((a, b) -> a + b + ref.age);*/

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType factoryType = MethodType.methodType(BinaryOperator.class, MyRef.class);
MethodType interfaceMethodType = MethodType.methodType(Object.class, Object.class, Object.class);
MethodType implementsMethodType = MethodType.methodType(Integer.class, MyRef.class, Integer.class, Integer.class);

MethodHandle implementsMethod = lookup.findStatic(TestLambda4.class, "lambda$main$1", implementsMethodType);

MethodType lambdaType = MethodType.methodType(Integer.class, Integer.class, Integer.class);
CallSite callSite = LambdaMetafactory.metafactory(lookup,
"apply", factoryType, interfaceMethodType,
implementsMethod,
lambdaType);

BinaryOperator<Integer> lambda = (BinaryOperator) callSite.getTarget().bindTo(new MyRef(20)).invoke();
test(lambda);
}

static Integer lambda$main$1(MyRef c, Integer a, Integer b) {
return a + b + c.age;
}

static void test(BinaryOperator<Integer> lambda) {
System.out.println(lambda.apply(1, 2));
}
}

与捕获基本类型变量类似,不过

除了

1
callSite.getTarget().invoke(new MyRef(20));

还可以

1
callSite.getTarget().bindTo(new MyRef(20)).invoke();

Stream 构建

自定义可切分迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class TestSpliterator {
static class MySpliterator<T> implements Spliterator<T> {
T[] array;
int begin;
int end;

public MySpliterator(T[] array, int begin, int end) {
this.array = array;
this.begin = begin;
this.end = end;
}

@Override
public boolean tryAdvance(Consumer<? super T> action) {
if (begin > end) {
return false;
}
action.accept(array[begin++]);
return true;
}

@Override
public Spliterator<T> trySplit() {
if (estimateSize() > 5) {
int mid = (begin + end) >>> 1;
MySpliterator<T> res = new MySpliterator<>(array, begin, mid);
System.out.println(Thread.currentThread().getName() + "=>" + res);
begin = mid + 1;
return res;
}
return null;
}

@Override
public String toString() {
return Arrays.toString(Arrays.copyOfRange(array, begin, end + 1));
}

@Override
public long estimateSize() {
return end - begin + 1;
}

@Override
public int characteristics() {
return Spliterator.SUBSIZED | Spliterator.ORDERED;
}
}

public static void main(String[] args) {
Integer[] all = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
MySpliterator<Integer> spliterator = new MySpliterator<>(all, 0, 9);


StreamSupport.stream(spliterator, false)
.parallel()
.forEach(x -> System.out.println(Thread.currentThread().getName() + ":" + x));
}
}

练习:按每次切分固定大小来实现

函数编程-实际应用 函数编程-速查表