上面的代码中,为什么可以直接在 reduce 方法中传入 Integer::sum 这个方法引用呢?这是因为 reduce 方法的入参就是 BinaryOperator 的函数式接口。
Optional reduce(BinaryOperator accumulator);
BinaryOperator
是继承自 BiFunction
,定义如下:
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
可以看到,只要是符合R apply(T t, U u)
; 的方法引用,都可以传入 reduce 中。可以是上面代码中的 Integer::sum
,也可以是 Integer::max
。
字节码
首先写 2 个 Lambda 方法:
public class LambdaMain {
public static void main(String[] args) {
new Thread(() -> System.out.println(“1”)).start();
IntStream.range(0, 5).boxed().filter(i -> i < 3).map(i -> i + “”).collect(Collectors.toList());
}
}
之后 javac LambdaMain.java
编译成字节码文件,再通过javap -p LambdaMain
输出 class 文件的所有类和成员,得到输出结果:
Compiled from “LambdaMain.java”
public class test.jdk.LambdaMain {
public test.jdk.LambdaMain();
public static void main(java.lang.String[]);
private static java.lang.String lambda$main$2(java.lang.Integer);
private static boolean lambda$main$1(java.lang.Integer);
private static void lambda$main$0();
}
输出的 void lambda$main$0()
对应的是() -> System.out.println("1")
输出的 boolean lambda$main$1(java.lang.Integer)
对应的是i -> i < 3
输出的 java.lang.String lambda$main$2(java.lang.Integer)
对应的是 i -> i + ""
我们可以看出 Lambda 表达式在 Java 8 中首先会生成一个 私有的静态函数
。
为什么不使用匿名内部类?
如果要在 Java 语言中实现 lambda 表达式,生成匿名内部类就可以轻松实现。但是 JDK 为什么没有这么实现呢?这是因为匿名内部类有一些缺点。
每个匿名内部类都会在 编译时 创建一个对应的 class 文件 ,在 运行时 不可避免的会有加载、验证、准备、解析、初始化等 类加载 过程。
每次调用都会创建一个这个 匿名内部类 class 的实例对象 ,无论是有状态的(使用到了外部的变量)还是无状态(没有使用外部变量)的内部类。
invokedynamic
本来要写文字的,但是俺发现俺总结的思维导图还挺清晰的,直接提出来吧,囧。
详情见 Class LambdaMetafactory 官方文档 ,java.lang.invoke.LambdaMetafactory#metafactory 的实现。
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(caller, invokedType,
invokedName, samMethodType,
implMethod, instantiatedMethodType,
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
其主要的概念有如下几个:
-
nvokedynamic 字节码指令:运行时 JVM 第一次到某个地方的这个指令的时候会进行 linkage,会调用用户指定的 Bootstrap Method 来决定要执行什么方法,之后便不需要这个步骤。
-
Bootstrap Method: 用户可以自己编写的方法,最终需要返回一个 CallSite 对象。
-
CallSite: 保存 MethodHandle 的容器,里面有一个 target MethodHandle。
MethodHandle: 真正要执行的方法的指针。
测试一下 Lambda 函数生成的字节码,为了方便起见,java 代码改成如下:
public class LambdaMain {
public static void main(String[] args) {
new Thread(() -> System.out.println(“1”)).start();
}
}
先编译成 class 文件,之后再反汇编javap -c -p LambdaMain
看下输出:
Compiled from “LambdaMain.java”
public class test.jdk.LambdaMain {
public test.jdk.LambdaMain();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.“”😦)V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
9: invokespecial #4 // Method java/lang/Thread.“”:(Ljava/lang/Runnable;)V
12: invokevirtual #5 // Method java/lang/Thread.start:()V
15: return
private static void lambda$main$0();
Code:
0: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #7 // String 1
5: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。
因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!
由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频
如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
2521338571)]
[外链图片转存中…(img-gqwMN0JY-1712521338571)]
[外链图片转存中…(img-OEDy8qAI-1712521338572)]
既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!
由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频
如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
[外链图片转存中…(img-ig8L4UQR-1712521338572)]