基本语法
Lambda
表达式的组成:
参数列表:即方法的参数列表,对应于 Runnable
接口的 run
方法,该方法没有参数,所以这里为空。
箭头符号:箭头符号将参数列表与 Lambda
表达式的主体分隔开。
Lambda
表达式的主体:即方法体,对应于 Runnable
接口的 run
方法的方法体。
new Thread(() -> System.out.println("Hello World")).start();
Lambda
表达式可以分为以下几种形式:
// 01、无参数,无返回值
() -> System.out.println("Hello World")
// 02、有一个参数,无返回值
(String name) -> System.out.println("Hello " + name)
// 03、有一个参数,无返回值,参数类型可以省略
name -> System.out.println("Hello " + name)
// 04、有多个参数,无返回值
(String name, int age) -> System.out.println("Hello " + name + ", age is " + age)
// 05、有多个参数,无返回值,参数类型可以省略
(name, age) -> System.out.println("Hello " + name + ", age is " + age)
// 06、有返回值
() -> {
System.out.println("Hello World");
return "Hello World";
}
// 07、有返回值,单条语句,return 与大括号可以省略
() -> "Hello World"
// 08、有返回值,单条语句,return 与大括号可以省略,参数类型可以省略
name -> "Hello " + name
// 09、有返回值,多条语句,return 与大括号不能省略
() -> {
System.out.println("Hello World");
return "Hello World";
}
变量的作用域
Lambda
表达式中可以访问外部的变量,但是有以下限制:
Lambda
表达式中访问的外部变量必须是 final
或者 effectively final
的。
Lambda
表达式中访问的外部变量不能被修改,即不能对外部变量进行赋值操作。
class Sample {
public static void main(String[] args) {
String value = "World";
Person person = (message) -> {
System.out.println(message + " " + value);
// ❌ value = "Java";
};
// 重新赋值,输出那里编译器会报错
value = "Java";
person.say("Hello");
}
}
方法重载
使用 Lambda
表达式时,调用一个类中的重载方法,且方法中的参数都为函数式接口时,编译器会报错。因为编译器无法推断 Lambda
表达式的类型。
@FunctionalInterface
interface MyInterfaceA {
void myMethod(String name);
}
@FunctionalInterface
interface MyInterfaceB {
void myMethod(String name);
}
class Sample {
public static void method(MyInterfaceA myInterfaceA) {
myInterfaceA.myMethod("Hello");
}
public static void method(MyInterfaceB myInterfaceb) {
myInterfaceb.myMethod("Hello");
}
public static void main(String[] args) {
// ⭕️ ❌编译报错,无法推断 Lambda 表达式的类型
method(str -> System.out.println(str + " World!"));
// ✅ 解决方法是显式指定 Lambda 表达式的类型。
method((MyInterfaceA) str -> System.out.println(str + " World!"));
method((MyInterfaceB) str -> System.out.println(str + " World!"));
}
}
方法引用
Lambda 表达式的方法引用是一种更简洁的 Lambda
表达式的写法,可以直接引用已有方法或者构造方法。
方法引用的语法是 类名::方法名
,其中 ::
是方法引用运算符,左边是类名或者对象名,右边是方法名。
方法引用的类型:
public class LambdaMethodReference {
public static void main(String[] args) {
// 01、静态方法引用
Consumer<String> consumer1 = LambdaMethodReference::print;
consumer1.accept("Hello World");
// 02、实例方法引用
LambdaMethodReference lambdaMethodReference = new LambdaMethodReference();
Consumer<String> consumer2 = lambdaMethodReference::println;
consumer2.accept("Hello World");
// 03、构造方法引用
Supplier<LambdaMethodReference> supplier = LambdaMethodReference::new;
LambdaMethodReference lambdaMethodReference1 = supplier.get();
}
}
// 定义一个接受三个参数的函数式接口 TriFunction
@FunctionalInterface
interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
class Sample {
public static void main(String[] args) {
List<Student> students = List.of(
new Student("张三", 10, "男"),
new Student("李四", 20, "男"),
new Student("王五", 30, "女")
);
// 静态方法引用
List<Student> filterPersons = Sample.filter(students, Sample::adult);
// 实例方法引用
List<Student> filter = Sample.filter(students, new Sample()::isAdult);
// 使用带参构造方法引用创建 Student 对象
TriFunction<String, Integer, String, Student> studentFactory = Student::new;
List<String> names = List.of("张三", "李四", "王五");
List<Integer> ages = List.of(20, 21, 22);
List<String> genders = List.of("男", "男", "女");
List<Student> studs = names.stream()
.map(name -> {
int index = names.indexOf(name);
return studentFactory.apply(name, ages.get(index), genders.get(index));
})
.toList();
}
static List<Student> filter(List<Student> students, Filter filter) {
List<Student> result = new ArrayList<>();
for (Student student : students) {
if (filter.call(student)) {
result.add(student);
}
}
return result;
}
static Boolean adult(Student student) {
return student.getAge() >= 18;
}
static void print(Student student) {
System.out.println(student);
}
public Boolean isAdult(Student student) {
return student.getAge() >= 18;
}
}
如果函数的参数超过2个,就需要自己创建一个函数式接口。
类型检查
Lambda
表达式的类型检查是通过上下文推断来实现的,Lambda
表达式的类型是通过上下文推断出来的,而不是通过 Lambda
表达式的参数类型来推断的。
// 01、通过上下文推断出 Lambda 表达式的类型是 Runnable
Runnable runnable = () -> System.out.println("Hello World");
// 02、通过上下文推断出 Lambda 表达式的类型是 Callable
Callable<String> callable = () -> "Hello World";
类型转换
Lambda
表达式的类型转换是通过强制类型转换来实现的,Lambda
表达式的类型是通过上下文推断出来的,而不是通过 Lambda
表达式的参数类型来推断的。
// 01、通过上下文推断出 Lambda 表达式的类型是 Runnable
Runnable runnable = () -> System.out.println("Hello World");
// 02、通过强制类型转换将 Lambda 表达式转换为 Callable 类型
Callable<String> callable = (Callable<String>) () -> "Hello World";
错误处理
Lambda
表达式中的代码可能会抛出异常,但是 Lambda
表达式中不能使用 throws
关键字来声明异常,因为函数式接口中的抽象方法没有声明异常。
public class LambdaException {
public static void main(String[] args) {
// Lambda 表达式中不能使用 throws 关键字
// Callable<String> callable = () -> { throw new Exception(); };
}
}
Lambda
表达式中的异常处理有以下几种方式:
使用 Callable
函数式接口,将 Lambda
表达式的返回值改为 Callable
类型,然后在 call
方法中捕获异常。
使用自定义函数式接口,将 Lambda 表达式的抽象方法声明异常。
public class LambdaException {
public static void main(String[] args) {
// 01、使用 try-catch 语句捕获异常
Callable<String> callable1 = () -> {
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
return "Hello World";
};
// 02、使用 Callable 函数式接口捕获异常
Callable<String> callable2 = new Callable<String>() {
@Override
public String call() {
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
return "Hello World";
}
};
// 03、使用自定义函数式接口捕获异常
MyCallable myCallable = () -> {
throw new Exception();
};
}
}