实现原理
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 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)); } }
|
其中
方法引用原理
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 {
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 {
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)); } }
|
练习:按每次切分固定大小来实现
函数编程-实际应用
函数编程-速查表