Lambda表达式
Lambda表达式是Java 8 引入的一个重要特性,它允许你以更简洁的方式定义匿名内部类的实例,尤其在函数式编程和处理集合数据时非常有用。Lambda表达式提供了一种紧凑、可读性更好的语法,用于传递方法作为参数,从而实现更灵活和简洁的代码。
请注意 Lambda表达式只适用于只有一个方法的接口或者抽象类
Lambda表达式的基本语法如下:
(parameters) -> expression
// 或者
(parameters) -> { statements; }
Lambda表达式的关键部分包括:
- 参数列表 (parameters) :Lambda表达式可以有零个、一个或多个参数。参数列表在小括号中定义,如果没有参数,可以直接使用
()表示。如果只有一个参数,可以省略小括号。 - 箭头符号 (->) :箭头符号用于将参数列表与Lambda表达式的主体分开。
- 主体 (expression or statements) :Lambda表达式的主体可以是单个表达式,也可以是一个代码块(使用大括号括起来的语句)。如果主体是单个表达式,那么该表达式的结果会被自动返回。如果主体是一个代码块,你需要显式使用
return语句来返回结果。
下面是一些Lambda表达式的示例:
- Lambda表达式没有参数:
() -> "Hello, World"
- Lambda表达式有一个参数:
(name) -> "Hello, " + name
- Lambda表达式有多个参数:
(a, b ,c) -> a + b + c
-
Lambda表达式包含多条语句的代码块:
(x, y) -> { int result = x + y; return result; }
Lambda表达式通常用于函数式接口的实现,这些接口只包含一个抽象方法。例如,java.util.function包中的接口,如 Consumer、Predicate、Function 等,通常用于Lambda表达式的参数类型。Lambda表达式可以传递给方法,或用于操作集合数据,例如在Java 8中引入的Stream API中。
以下是一些示例,展示如何在不同的上下文中使用Lambda表达式:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 使用Lambda表达式进行集合遍历和打印
names.forEach(name -> System.out.println("Hello, " + name));
// 使用Lambda表达式进行筛选
List<String> longNames = names.stream()
.filter(name -> name.length() > 5)
.collect(Collectors.toList());
// 使用Lambda表达式自定义排序
Collections.sort(names, (a, b) -> a.compareTo(b));
Lambda表达式使Java更加接近函数式编程的风格,提高了代码的可读性和简洁性,特别是在处理集合、事件处理和并发编程等方面。
方法引用
Java 中的方法引用(Method References)是一种简化Lambda表达式的语法,用于调用已经存在的方法。方法引用可以替代某些简单的Lambda表达式,使代码更加简洁和易读。方法引用允许你将方法作为值传递,而不是编写Lambda表达式。
在方法引用中,你提供了对已经存在的方法的引用,而不是提供一个具体的方法体。方法引用通常用于函数式接口(Functional Interface)的实现,这些接口定义了一个抽象方法,可以通过方法引用来实现。方法引用的语法格式是方法的引用类型::方法名,
其中方法的引用类型可以是:
-
静态方法引用:引用静态方法。
ClassName::staticMethodName -
实例方法引用:引用特定对象的实例方法。
object::instanceMethodName -
类名引用:引用特定类型的任意对象的实例方法。
ClassName::instanceMethodName -
构造函数引用:引用构造函数。
ClassName::new
使用示例
public class MethodReferencesExamples {
public static <T> T mergeThings(T a, T b, BiFunction<T, T, T> merger) {
return merger.apply(a, b);
}
public static <T, R> R createString(T a, Function<T, R> merger) {
return merger.apply(a);
}
public static String appendStrings(String a, String b) {
return a + b;
}
public String appendStrings2(String a, String b) {
return a + b;
}
public static void main(String[] args) {
MethodReferencesExamples myApp = new MethodReferencesExamples();
// Calling the method mergeThings with a lambda expression
System.out.println(MethodReferencesExamples.
mergeThings("Hello ", "World!", (a, b) -> a + b));
// Reference to a static method
System.out.println(MethodReferencesExamples.
mergeThings("Hello ", "World!", MethodReferencesExamples::appendStrings));
// Reference to an instance method of a particular object
System.out.println(MethodReferencesExamples.
mergeThings("Hello ", "World!", myApp::appendStrings2));
// Reference to an instance method of an arbitrary object of a
// particular type
System.out.println(MethodReferencesExamples.
mergeThings("Hello ", "World!", String::concat));
final Function<byte[], String> convert = String::new;
System.out.println(MethodReferencesExamples.
createString("Hello World!".getBytes(), convert));
}
}
输出
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
原理
使用示例
package online.greatfeng;
public interface RunnableFactory {
Runnable newRunnable();
}
package online.greatfeng;
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.lambda(main::getRunnable);
}
public void lambda(RunnableFactory factory) {
System.out.println("RunnableFactory.class : " + factory.getClass());
final Runnable runnable = factory.newRunnable();
System.out.println("Runnable.class : " + runnable.getClass());
runnable.run();
}
public Runnable getRunnable() {
return () -> System.out.println("Hello Lambda!");
}
}
添加虚拟机参数并运行
-Djdk.internal.lambda.dumpProxyClasses=.
-Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true
会动态生成两个class文件
Main$$Lambda.0x00000007c00c8c28.class
newRunnable()会调用main.getRunnable()方法返回一个 Runnable 对象
Main$$Lambda.0x00000007c00c9c00.class
这个 Runnable 的 run 方法会调用 编译器生成的 Main 的静态方法 lambda$getRunnable$0()
运行输入
RunnableFactory.class : class online.greatfeng.Main$$Lambda/0x00000007c00c8c28
Runnable.class : class online.greatfeng.Main$$Lambda/0x00000007c00c9c00
Hello Lambda!
可通过本人写的这个类库打印 Main.class 字节码 ClassParser
可看出 Main.java 通过javac编译器 生成 Main.class 文件时添加了一个辅助方法
标记1
- access_flags : ACC_SYNTHETIC 标记代表是编译器生成,不是源代码文件中有的,方法名为
- name_index :
lambda$getRunnable$0为方法名
标记2: CodeAttribute 属性中只打印了一个Hello Lambda! 就是 getRunnable 的方法体
invokedynamic 指令
invokedynamic 是 Java 虚拟机(JVM)指令的一种,引入于 Java 7,为了提高 JVM 在处理动态语言和函数式编程时的灵活性和性能。主要用于支持动态类型语言、Lambda 表达式和其他需要在运行时进行方法绑定的情况。
下面是关于 invokedynamic 字节码指令的一些基本介绍:
- 指令格式:
invokedynamic字节码指令的格式如下:
invokedynamic CONSTANT_InvokeDynamic_info 常量池的索引
- CONSTANT_InvokeDynamic_info: 包含
- 一个 BootstrapMethod
- 方法签名
main() 方法的字节码
#1 MethodInfo{
access_flags 9 ACC_PUBLIC ACC_STATIC
name_index #71 = CpInfoUtf8(data = main)
descriptor_index #72 = CpInfoUtf8(data = ([Ljava/lang/String;)V) attributes_count 1 attributes = attributes[0] =
CodeAttribute{
max_stack = 3
max_locals = 2
codes =
code[0] = 187 new args[#0] = CpInfoClass(data = online/greatfeng/Main)
code[3] = 89 dup
code[4] = 183 invokespecial args[#0] = CpInfoMethodref(classIndex = #7 nameAndTypeIndex = #3 data = online/greatfeng/Main . <init> # ()V)
code[7] = 76 astore_1
code[8] = 43 aload_1
code[9] = 43 aload_1
code[10] = 89 dup
code[11] = 184 invokestatic args[#0] = CpInfoMethodref(classIndex = #11 nameAndTypeIndex = #12 data = java/util/Objects . requireNonNull # (Ljava/lang/Object;)Ljava/lang/Object;)
code[14] = 87 pop
code[15] = 186 invokedynamic args[#0] = CpInfoInvokeDynamic(bootstrapMethodAttrIndex = #0 ,nameAndTypeIndex = #17 ,data = CpInfoNameAndType(nameIndex = #18, descriptorIndex = #19, data = newRunnable # (Lonline/greatfeng/Main;)Lonline/greatfeng/RunnableFactory; )) args[#1] = null
code[20] = 182 invokevirtual args[#0] = CpInfoMethodref(classIndex = #7 nameAndTypeIndex = #21 data = online/greatfeng/Main . lambda # (Lonline/greatfeng/RunnableFactory;)V)
code[23] = 177 Return
}
code[15] 有一条指令 invokedynamic
bootstrapMethodAttrIndex = #0
查看引导方法表 BootstrapMethods ,为 LambdaMetafactory.metafactory() 静态方法生成一个调用点 CallSite
- 方法签名,方法名为 newRunnable 参数为 Main; ,返回值为 RunnableFactory;
相当于
new Main$$Lambda.0x00000007c00c8c28.class(this)
getRunnable()方法的字节码
#3 MethodInfo{
access_flags 1 ACC_PUBLIC
name_index #79 = CpInfoUtf8(data = getRunnable)
descriptor_index #54 = CpInfoUtf8(data = ()Ljava/lang/Runnable;)
attributes_count 1
attributes = attributes[0] =
CodeAttribute{
max_stack = 1
max_locals = 1
codes =
code[0] = 186 invokedynamic args[#0] = CpInfoInvokeDynamic(bootstrapMethodAttrIndex = #3 ,nameAndTypeIndex = #63 ,data = CpInfoNameAndType(nameIndex = #61, descriptorIndex = #54, data = run # ()Ljava/lang/Runnable; )) args[#1] = null
code[5] = 176 areturn
}
}
code[0] 有一条指令 invokedynamic
bootstrapMethodAttrIndex = #3
查看引导方法表 BootstrapMethods ,为 LambdaMetafactory.metafactory() 静态方法生成一个调用点 CallSite
- 方法签名,方法名为 run 参数为为空() ,返回值为 Runnable;相当于
new Main$$Lambda.0x00000007c00c9c00.class()
LambdaMetafactory
public final class LambdaMetafactory {
public static CallSite metafactory(MethodHandles.Lookup caller,
String interfaceMethodName,
MethodType factoryType,
MethodType interfaceMethodType,
MethodHandle implementation,
MethodType dynamicMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(Objects.requireNonNull(caller),
Objects.requireNonNull(factoryType),
Objects.requireNonNull(interfaceMethodName),
Objects.requireNonNull(interfaceMethodType),
Objects.requireNonNull(implementation),
Objects.requireNonNull(dynamicMethodType),
false,
EMPTY_CLASS_ARRAY,
EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
}
引导方法表 BootstrapMethods
BootstrapMethod[0] =
BootstrapMethod(bootstrap_method_ref=#96 = CpInfoMethodHandle(referenceKind = REF_invokeStatic ,referenceIndex = #97 data = REF_invokeStatic . java/lang/invoke/LambdaMetafactory . metafactory # (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;)
bootstrap_arguments=
bootstrap_argument[0] = #84 +CpInfoMethodType(descriptorIndex = #54 ,data = ()Ljava/lang/Runnable;)
bootstrap_argument[1] = #85 +CpInfoMethodHandle(referenceKind = REF_invokeVirtual ,referenceIndex = #86 data = REF_invokeVirtual . online/greatfeng/Main . getRunnable # ()Ljava/lang/Runnable;)
bootstrap_argument[2] = #84 +CpInfoMethodType(descriptorIndex = #54 ,data = ()Ljava/lang/Runnable;)
BootstrapMethod[1] =
BootstrapMethod(bootstrap_method_ref=#103 = CpInfoMethodHandle(referenceKind = REF_invokeStatic ,referenceIndex = #104 data = REF_invokeStatic . java/lang/invoke/StringConcatFactory . makeConcatWithConstants # (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;)
bootstrap_arguments=
ootstrap_argument[0] = #88 +CpInfoString(index = #89 data = RunnableFactory.class : )
BootstrapMethod[2] =
BootstrapMethod(bootstrap_method_ref=#103 = CpInfoMethodHandle(referenceKind = REF_invokeStatic ,referenceIndex = #104 data = REF_invokeStatic . java/lang/invoke/StringConcatFactory . makeConcatWithConstants # (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;)
bootstrap_arguments=
bootstrap_argument[0] = #90 +CpInfoString(index = #91 data = Runnable.class : )
BootstrapMethod[3] =
BootstrapMethod(bootstrap_method_ref=#96 = CpInfoMethodHandle(referenceKind = REF_invokeStatic ,referenceIndex = #97 data = REF_invokeStatic . java/lang/invoke/LambdaMetafactory . metafactory # (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;)
bootstrap_arguments=
bootstrap_argument[0] = #92 +CpInfoMethodType(descriptorIndex = #6 ,data = ()V)
bootstrap_argument[1] = #93 +CpInfoMethodHandle(referenceKind = REF_invokeStatic ,referenceIndex = #94 data = REF_invokeStatic . online/greatfeng/Main . lambda$getRunnable$0 # ()V)
bootstrap_argument[2] = #92 +CpInfoMethodType(descriptorIndex = #6 ,data = ()V)
最终这段代码可以翻译为
package online.greatfeng;
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.lambda(new online.greatfeng.Main$$Lambda/0x00000007c00c8c28(this));
}
public void lambda(RunnableFactory factory) {
System.out.println("RunnableFactory.class : " + factory.getClass());
final Runnable runnable = factory.newRunnable();
System.out.println("Runnable.class : " + runnable.getClass());
runnable.run();
}
public Runnable getRunnable() {
return online.greatfeng.Main$$Lambda/0x00000007c00c9c00();
}
private static void lambda$getRunnable$0(){
System.out.println("Hello Lambda!");
}
}