Java Lambda 表达式

Java 8引入了Lambda表达式,这是一项令人激动的功能,它为Java开发人员提供了一种简洁而强大的编码方式。本文将深入探讨Java Lambda表达式的概念、语法和使用方法,帮助你充分理解这一重要的特性。

简介

Lambda表达式是一种匿名函数,它可以作为方法参数传递,并且可以用来简化编写函数式接口的代码。Lambda表达式可以在不创建类的情况下实现函数式编程的特性。

语法

Lambda表达式的语法由箭头"->"分割为两部分:参数列表和函数体。参数列表指定了传递给Lambda表达式的参数,而函数体则定义了Lambda表达式的具体执行逻辑。

示例:

(int x, int y) -> x + y

这个Lambda表达式接受两个整数参数x和y,并返回它们的和。

函数式接口

Lambda表达式主要用于函数式接口。函数式接口是指只有一个抽象方法的接口。Lambda表达式可以直接赋值给函数式接口的变量,从而实现对接口的实现。

示例:

interface MyInterface {
    int operation(int x, int y);
}

MyInterface add = (int x, int y) -> x + y;

在这个例子中,我们定义了一个函数式接口MyInterface,它有一个operation方法。然后,我们使用Lambda表达式实现了这个接口,将其赋值给变量add

Java 内置函数式接口

在Java中,有一些常见的内置函数式接口,它们是为了方便使用Lambda表达式而设计的。以下是一些常见的内置函数式接口:

  1. Runnable:表示一个没有参数和返回值的操作。它常用于多线程编程。
  2. Supplier<T>:表示一个提供(返回)类型为T的值的操作。它不接受参数,但提供一个结果。
  3. Consumer<T>:表示接受一个参数类型为T的值,并对其进行操作而不返回结果的操作。
  4. Function<T, R>:表示接受一个参数类型为T的值,并返回一个结果类型为R的值的操作。
  5. Predicate<T>:表示接受一个参数类型为T的值,并返回一个布尔值的操作。它用于判断某个条件是否成立。
  6. UnaryOperator<T>:表示接受一个参数类型为T的值,并返回一个相同类型的结果的操作。它是Function<T, T>的子接口。

Java中还有一些以"Bi"开头的函数式接口,它们主要用于接受两个参数的情况。这些函数式接口的命名约定是以"Bi"作为前缀,表示接受两个参数的操作。以下是一些常见的以"Bi"开头的函数式接口:

  1. BiConsumer<T, U>:表示接受两个参数类型分别为T和U的值,并对其进行操作而不返回结果的操作。
  2. BiFunction<T, U, R>:表示接受两个参数类型分别为T和U的值,并返回一个结果类型为R的值的操作。
  3. BinaryOperator<T>:表示接受两个相同类型的参数,并返回一个相同类型的结果的操作。
  4. BiPredicate<T, U>:表示接受两个参数类型分别为T和U的值,并返回一个布尔值的操作。用于判断某个条件是否成立。
  5. BiConsumer<T, U>:表示接受两个参数类型分别为T和U的值,并对其进行操作而不返回结果的操作。

这些以"Bi"开头的函数式接口在需要处理两个参数的情况下非常有用。它们提供了一种简洁的方式来编写带有多个参数的Lambda表达式,使代码更加清晰和易读。

方法引用

方法引用是 Java 中一种简化 Lambda 表达式的语法,它允许你直接引用现有方法作为 Lambda 表达式的实现。方法引用可以使代码更简洁、易读和模块化。它通常用于函数式接口的实现,可以将现有方法作为 Lambda 表达式传递。

在 Java 中,有四种方法引用的形式:

静态方法引用

引用静态方法的方法引用形式为类名::静态方法名。当我们使用静态方法引用时,我们可以引用现有类中的静态方法。

以下是一个关于静态方法引用的简单示例:

假设有一个函数式接口Converter,它具有一个抽象方法int convert(String str),用于将字符串转换为整数。我们可以使用静态方法引用来引用 Integer 类中的 parseInt 方法,将其作为 Converter 接口的实现。

首先,我们定义Converter接口:

@FunctionalInterface
interface Converter {
    int convert(String str);
}

然后,我们使用静态方法引用来实现这个接口,引用Integer类的parseInt方法:

Converter converter = Integer::parseInt;

现在,我们可以使用converter对象将字符串转换为整数,而无需编写冗长的 Lambda 表达式:

int result = converter.convert("123");
System.out.println(result); // 输出:123

在这个例子中,我们通过静态方法引用Integer::parseIntparseInt方法作为Converter接口的实现。这使得我们能够使用converter对象将字符串转换为整数,而无需显式编写 Lambda 表达式或定义匿名类。方法引用帮助我们简化了代码,使其更加清晰和易读。

实例方法引用:

实例方法引用是通过引用现有对象的实例方法来创建 Lambda 表达式。语法形式为实例::方法名。在实例方法引用中,Lambda 表达式的第一个参数将成为方法调用的目标对象,并在调用方法时使用。以下是一个关于实例方法引用的简单示例:

假设我们有一个自定义的类Person,其中包含了实例方法getName,用于获取人物的姓名。我们可以使用实例方法引用来引用该方法,并将其作为函数式接口的实现。

首先,我们定义一个函数式接口NameExtractor,它具有一个抽象方法String extractName(Person person),用于从Person对象中提取姓名。

@FunctionalInterface
interface NameExtractor {
    String extractName(Person person);
}

@AllArgsConstructor
public class Person {
    private String name;

    public String getName(Person person){
        return person.name;
    }
}

然后,我们创建一个Person对象,并使用实例方法引用引用其getName方法来实现NameExtractor接口:

Person person = new Person("John");
NameExtractor extractor = person::getName;

现在,我们可以使用extractor对象从person对象中提取姓名,而无需编写冗长的 Lambda 表达式:

String name = extractor.extractName(person);
System.out.println(name); // 输出:John

在这个例子中,我们通过实例方法引用person::getNamegetName方法作为NameExtractor接口的实现。这使得我们能够使用extractor对象从person对象中提取姓名,而无需显式编写Lambda表达式或定义匿名类。实例方法引用简化了代码,使其更加清晰和易读。

对象方法引用

当使用对象方法引用时,我们可以引用现有对象的实例方法作为 Lambda 表达式的实现。以下是一个关于对象方法引用的简单示例:

假设我们有一个自定义的类Person,其中包含了实例方法getName,用于获取人物的姓名。我们可以使用实例方法引用来引用该方法,并将其作为函数式接口的实现。

首先,我们定义一个函数式接口NameExtractor,它具有一个抽象方法String extractName(Person person),用于从Person对象中提取姓名。

@FunctionalInterface
interface NameExtractor {
    String extractName(Person person);
}

@AllArgsConstructor
public class Person {
    private String name;

    // 注意:getName 方法比 NameExtractor 中的 extractName 少了一个参数,而这个参数正好是 Person 类型的
    public String getName(){
        return this.name;
    }
}

然后,我们创建一个Person对象,并使用对象方法引用引用其getName方法来实现NameExtractor接口:

Person person = new Person("Jon");
NameExtractor extractor = Person::getName;

现在,我们可以使用extractor对象从person对象中提取姓名,而无需编写冗长的 Lambda 表达式:

String name = extractor.extractName(person);
System.out.println(name); // 输出:John

在这个例子中,我们通过对象方法引用person::getNamegetName方法作为NameExtractor接口的实现。这使得我们能够使用messagePrinter对象打印消息,而无需显式编写Lambda表达式或定义匿名类。对象方法引用简化了代码,使其更加清晰和易读。

构造方法引用

当使用构造方法引用时,可以使用类名::new的语法形式来引用类的构造方法。以下是一个简单的示例:

@FunctionalInterface
interface PersonFactory {
    Person create(String name, int age);
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class Example {
    public static void main(String[] args) {
        // 构造方法引用示例:引用 Person 类的构造方法
        PersonFactory personFactory = Person::new;
        Person person = personFactory.create("John", 30);
        System.out.println(person.getName());  // 输出:John
        System.out.println(person.getAge());   // 输出:30
    }
}

在这个示例中,我们定义了一个PersonFactory函数式接口,它具有一个create方法用于创建Person对象。然后,我们使用构造方法引用Person::newPerson类的构造方法作为PersonFactory接口的实现。通过调用personFactory.create("John", 30),我们使用构造方法引用创建了一个新的Person对象。

构造方法引用非常有用,可以将它用于各种场景,例如在函数式接口中定义创建对象的方法,或者作为流操作的构造方法参数等。它简化了对象的创建过程,并使代码更加简洁和可读。

总结

方法引用可以减少冗余的代码,并提高代码的可读性。它能够更清晰地表达代码的意图,使代码更具可维护性和可重用性。通过使用方法引用,可以将重复的逻辑封装到现有方法中,并在需要时直接引用该方法,而无需编写冗长的Lambda表达式。

优势

  • 简洁性:Lambda表达式可以大大减少代码量,使代码更加简洁易读。
  • 函数式编程:Lambda表达式支持函数式编程,可以使用函数作为参数和返回值,使代码更加灵活和模块化。
  • 并行处理:Lambda表达式可以方便地与Java 8的Stream API一起使用,实现并行处理和操作集合的能力。

应用场景

  • 集合操作:Lambda表达式可以与集合类一起使用,方便地进行过滤、映射和归约等操作。
  • 线程和并发:Lambda表达式可以简化线程和并发编程的代码,使其更加清晰和易于理解。

项目实战

基于 Spring Boot 2.7.12、MyBatis-Plus、Spring Security 等主流技术栈构建的后台管理系统:

Gitee GitHub
后端 https://gitee.com/linjiabin100/pi-admin.git https://github.com/zengpi/pi-admin.git
前端 https://gitee.com/linjiabin100/pi-admin-web.git https://github.com/zengpi/pi-admin-web.git
posted @ 2023-06-19 18:49  ZnPi  阅读(39)  评论(0编辑  收藏  举报