一、
什么是 Lambda 表达式?
Lambda 表达式是 JDK 8 引入的一种新的语法特性,它允许我们将函数作为参数传递给方法,或者将代码视为数据。Lambda 表达式的基本形式是:
(parameters) -> expression
//或者
(parameters) -> { statements; }
为什么要用Lambda 表达式
- 简化代码:Lambda 表达式可以替代匿名内部类,使代码更加简洁。
- 函数式编程:Lambda 表达式支持函数式编程,使得 Java 可以更好地处理集合数据。
- 提高可读性:通过 Lambda 表达式,代码的可读性得到了显著提升。
示例:
假设我们有一个 Person
类,包含 name
和 age
两个属性。我们希望对一个 Person
列表进行排序,并打印出排序后的结果。
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
public class LambdaExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
);
// 使用 Lambda 表达式进行排序
Collections.sort(people, (p1, p2) -> p1.name.compareTo(p2.name));
// 打印排序后的结果
people.forEach(System.out::println);
}
}
在这个例子中,我们使用 Lambda 表达式
(p1, p2) -> p1.name.compareTo(p2.name)
作为Comparator
传递给Collections.sort
方法,实现了对Person
列表按名字排序的功能。
二、
使用 Lambda 表达式的前提条件:
Lambda 表达式只能用于函数式接口(Functional Interface),即只有一个抽象方法的接口。常见的函数式接口包括 Runnable
、Comparator
、Predicate
等。
Lambda 表达式的书写规则:
- 参数列表:如果只有一个参数,可以省略括号
()
。如果没有参数,必须使用空括号()
。 - 箭头:使用
->
将参数列表与 Lambda 体分隔开。 - Lambda 体:如果 Lambda 体只有一条语句,可以省略大括号
{}
和return
关键字。如果有多条语句,必须使用大括号{}
并明确使用return
关键字。
Lambda 表达式省略书写的规则:
- 类型推断:编译器可以根据上下文推断参数类型,因此可以省略参数类型。
- 单个参数省略括号:如果只有一个参数,可以省略括号
()
。 - 单条语句省略大括号:如果 Lambda 体只有一条语句,可以省略大括号
{}
和return
关键字。
示例代码:
有参数的 Lambda 表达式:
// 正常书写
Comparator<Person> comparator1 = (Person p1, Person p2) -> {
return p1.name.compareTo(p2.name);
};
// 省略书写
Comparator<Person> comparator2 = (p1, p2) -> p1.name.compareTo(p2.name);
无参数的 Lambda 表达式:
// 正常书写
Runnable runnable1 = () -> {
System.out.println("Hello, Lambda!");
};
// 省略书写
Runnable runnable2 = () -> System.out.println("Hello, Lambda!");
三、
Lambda 表达式底层实现原理(了解即可)
Lambda 表达式的底层实现依赖于 Java 的 invokedynamic
指令和 MethodHandle
机制。编译器会将 Lambda 表达式转换为一个私有的静态方法,并通过 invokedynamic
指令在运行时动态绑定该方法。
解释:
- invokedynamic 指令:这是 Java 7 引入的一种新的字节码指令,用于支持动态语言的运行时绑定。Lambda 表达式利用这个指令在运行时动态绑定方法。
- MethodHandle:这是一个类似于反射的机制,但它更加高效和灵活。Lambda 表达式在底层会使用
MethodHandle
来调用生成的静态方法。
Lambda 表达式在编译时会被转换为一个静态方法,并在运行时通过 invokedynamic
指令动态绑定这个方法,从而实现高效的函数式编程。
四、
Lambda 表达式与匿名内部类的区别
底层实现原理的区别:
- 匿名内部类:编译器会为每个匿名内部类生成一个新的类文件,并在运行时加载这些类文件。这会导致额外的类加载开销和内存占用。
- Lambda 表达式:编译器会将 Lambda 表达式转换为一个私有的静态方法,并通过
invokedynamic
指令在运行时动态绑定该方法,避免了生成额外的类文件,从而提高了性能。
书写区别:
- 匿名内部类:需要显式地实现接口或继承类,代码较为冗长。
- Lambda 表达式:代码简洁,省略了接口名称和方法名称。
使用条件的区别:
- 匿名内部类:可以用于任何接口或类,不限于函数式接口。
- Lambda 表达式:只能用于函数式接口,即只有一个抽象方法的接口
示例1:
// 使用匿名内部类实现 Runnable 接口
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello, Anonymous Inner Class!");
}
};
runnable1.run();
// 使用 Lambda 表达式实现 Runnable 接口
Runnable runnable2 = () -> System.out.println("Hello, Lambda!");
runnable2.run();
示例2:
假设我们有一个 Calculator
接口,包含一个 int calculate(int a, int b)
方法。我们可以使用匿名内部类和 Lambda 表达式分别实现这个接口。
interface Calculator {
int calculate(int a, int b);
}
public class LambdaVsAnonymous {
public static void main(String[] args) {
// 使用匿名内部类实现 Calculator 接口
Calculator calculator1 = new Calculator() {
@Override
public int calculate(int a, int b) {
return a + b;
}
};
System.out.println("Anonymous Inner Class: " + calculator1.calculate(10, 20));
// 使用 Lambda 表达式实现 Calculator 接口
Calculator calculator2 = (a, b) -> a + b;
System.out.println("Lambda Expression: " + calculator2.calculate(10, 20));
}
}
总结:
看到这里我相信你肯定能感受到,Lambda 表达式在代码简洁性和可读性方面具有明显优势。
掌握jdk8的一些新增特性,对我们是很加分的也能提升技术,提高编程效率,希望对你有帮助。