# Lambda 表达式语法

## 基本语法

`Lambda` 表达式的组成：

1. 参数列表：即方法的参数列表，对应于 `Runnable` 接口的 `run` 方法，该方法没有参数，所以这里为空。
2. 箭头符号：箭头符号将参数列表与 `Lambda` 表达式的主体分隔开。
3. `Lambda` 表达式的主体：即方法体，对应于 `Runnable` 接口的 `run` 方法的方法体。

```java
new Thread(() -> System.out.println("Hello World")).start();
```

`Lambda` 表达式可以分为以下几种形式：

```java
// 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` 表达式中访问的外部变量不能被修改，即不能对外部变量进行赋值操作。

```java
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` 表达式的类型。

```java
@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`

```java
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();
    }
}
```

```java
// 定义一个接受三个参数的函数式接口 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` 表达式的参数类型来推断的。

```java
// 01、通过上下文推断出 Lambda 表达式的类型是 Runnable
Runnable runnable = () -> System.out.println("Hello World");

// 02、通过上下文推断出 Lambda 表达式的类型是 Callable
Callable<String> callable = () -> "Hello World";
```

## 类型转换

`Lambda` 表达式的类型转换是通过强制类型转换来实现的，`Lambda` 表达式的类型是通过上下文推断出来的，而不是通过 `Lambda` 表达式的参数类型来推断的。

```java
// 01、通过上下文推断出 Lambda 表达式的类型是 Runnable
Runnable runnable = () -> System.out.println("Hello World");

// 02、通过强制类型转换将 Lambda 表达式转换为 Callable 类型
Callable<String> callable = (Callable<String>) () -> "Hello World";
```

## 错误处理

`Lambda` 表达式中的代码可能会抛出异常，但是 `Lambda` 表达式中不能使用 `throws` 关键字来声明异常，因为函数式接口中的抽象方法没有声明异常。

```java
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 表达式的抽象方法声明异常。

```java
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();
        };
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://coderlzw.gitbook.io/backend/lambda-biao-da-shi/lambda-biao-da-shi-yu-fa.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
