Lambda 表达式语法

基本语法

Lambda 表达式的组成:

  1. 参数列表:即方法的参数列表,对应于 Runnable 接口的 run 方法,该方法没有参数,所以这里为空。

  2. 箭头符号:箭头符号将参数列表与 Lambda 表达式的主体分隔开。

  3. 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 表达式中可以访问外部的变量,但是有以下限制:

  1. Lambda 表达式中访问的外部变量必须是 final 或者 effectively final 的。

  2. 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 表达式的写法,可以直接引用已有方法或者构造方法。

方法引用的语法是 类名::方法名,其中 :: 是方法引用运算符,左边是类名或者对象名,右边是方法名。

方法引用的类型:

  1. 静态方法引用:类名::静态方法

  2. 实例方法引用:对象名::实例方法

  3. 构造方法引用:类名::new

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 表达式中的异常处理有以下几种方式:

  1. 使用 try-catch 语句捕获异常。

  2. 使用 Callable 函数式接口,将 Lambda 表达式的返回值改为 Callable 类型,然后在 call 方法中捕获异常。

  3. 使用自定义函数式接口,将 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();
        };
    }
}

最后更新于

这有帮助吗?