30、Java基础教程之新特性·lambda表达式

  • 1️⃣ 概念
  • 2️⃣ 优势和缺点
  • 3️⃣ 使用
    • 3.1 语法结构
  • 3.2 案例
    • 3.2.1 无参Lambda
    • 3.2.2 带有一个参数
    • 3.2.3 带有多个参数
    • 3.2.4 方法引用的简化形式
  • 4️⃣ 应用场景
  • 5️⃣ 优化技巧
  • 6️⃣ 原理
  • 7️⃣ 注意性能问题
  • * 总结

*

1️⃣ 概念

Java Lambda表达式是在Java 8中引入的一项重要特性。它们主要受到函数式编程语言的影响,如HaskellScala。Lambda表达式为Java引入了一种简洁而强大的功能,可以更便捷地处理函数式编程的概念。

Lambda表达式是一个匿名函数,可以传递给方法作为参数或用作返回值。它是一个使用箭头符号(->)定义的代码块,由Lambda参数、箭头符号和方法体组成。 Lambda表达式允许我们将行为作为一等公民进行传递,使得代码更具可读性和灵活性。

2️⃣ 优势和缺点

优点:

  • 简洁性:Lambda表达式允许以更紧凑的方式编写代码,减少样板代码;
  • 使代码更易读:通过使用Lambda表达式,可以将关注点放在实际要执行的操作上,减少了对底层实现的关注;
  • 支持函数式编程风格:能够更好地支持函数式编程的思想,如高阶函数、闭包等。

缺点:

  • 需要理解函数式编程的概念:Lambda表达式需要开发者掌握函数式编程的思想,在初步学习过程中可能较为陌生;
  • 可能引发性能问题:Lambda表达式在某些情况下可能导致额外的开销和性能损失,特别是与传统的循环和条件语句相比。

3️⃣ 使用

3.1 语法结构

Lambda表达式的语法结构如下:

(parameters) -> expression or {
   
     statements}

这是最基本的Lambda表达式结构,其中,parameters表示方法参数列表,可以为空或包含一个或多个参数,expression或者{statements}表示要执行的表达式或代码块,用代码块来表示多个语句时,可以在其中包含多条语句并使用return语句返回结果。

而针对不同的情况,Java中Lambda表达式的语法结构有以下几种写法:

1、 无参情况:当Lambda表达式不需要参数时,可以使用一对空括号代替参数列表,后面可以跟一个表达式或一个代码块;

() -> expression or {
     
       statements}

2、 带有一个参数的情况:当Lambda表达式只有一个参数时,可以省略参数列表的括号;

parameter -> expression or {
     
       statements}

3、 带有多个参数的情况:;

(parameters) -> expression or {
     
       statements}

4、 方法引用的简化形式:如果Lambda表达式的主体只是调用一个方法,可以使用方法引用的简化形式来代替具体的Lambda表达式,其中Class是静态方法所在的类名,object是实例方法所在的对象;

Class::methodName
object::methodName

这些是Lambda表达式的常见写法,在应用程序中根据需求和上下文选择合适的写法。

关于方法引用的知识在后边的文章再具体介绍。

3.2 案例

3.2.1 无参Lambda

public class LambdaExample {
   
     
    public static void main(String[] args) {
   
     
        // 1. 使用Lambda表达式实现Runnable接口
        Runnable runnable = () -> System.out.println("Hello, World!");
        Thread thread = new Thread(runnable);
        thread.start();
        
        // 2. 使用Lambda表达式实现自定义函数式接口
        MyFunctionalInterface funcInterface = () -> System.out.println("Hello, ChinaAi!");
        funcInterface.run();
    }
}

@FunctionalInterface
interface MyFunctionalInterface {
   
     
    void run();
}

在上面代码中,我使用Lambda表达式实现Runnable接口:创建一个Runnable对象,Lambda表达式表示了在run()方法中要执行的代码块。

然后,我使用Lambda表达式实现自定义的函数式接口MyFunctionalInterface:创建一个实现该接口的对象,Lambda表达式表示了在run()方法中要执行的代码块。

运行结果:

Hello, World!
Hello, ChinaAi!

3.2.2 带有一个参数

import java.util.function.Consumer;

public class LambdaExample {
   
     
    public static void main(String[] args) {
   
     
        // 1. 使用Lambda表达式实现Consumer接口
        Consumer<String> consumer = (name) -> System.out.println("Hello, " + name);
        consumer.accept("Alice");
        
        // 2. 使用Lambda表达式实现自定义函数式接口
        MyFunctionalInterface funcInterface = (name) -> System.out.println("Hello, " + name);
        funcInterface.run("Bob");
    }
}

@FunctionalInterface
interface MyFunctionalInterface {
   
     
    void run(String name);
}

在上面代码中,我使用Lambda表达式实现Consumer接口:创建一个Consumer对象,Lambda表达式表示了在accept()方法中要执行的代码块。

然后,我使用Lambda表达式实现自定义的函数式接口MyFunctionalInterface:创建一个实现该接口的对象,Lambda表达式表示了在run(String name)方法中要执行的代码块。

运行结果:

Hello, Alice
Hello, Bob

3.2.3 带有多个参数

import java.util.Comparator;

public class LambdaExample {
   
     
    public static void main(String[] args) {
   
     
        // 1. 使用Lambda表达式实现Comparator接口
        Comparator<Integer> comparator = (num1, num2) -> Integer.compare(num1, num2);
        int result = comparator.compare(5, 3);
        System.out.println(result);
        
        // 2. 使用Lambda表达式实现自定义函数式接口
        MyFunctionalInterface funcInterface = (num1, num2) -> System.out.println("Sum: " + (num1 + num2));
        funcInterface.run(10, 20);
    }
}

@FunctionalInterface
interface MyFunctionalInterface {
   
     
    void run(int num1, int num2);
}

在上面代码中,我使用Lambda表达式实现Comparator接口:创建一个Comparator对象,Lambda表达式表示了在compare(num1, num2)方法中要执行的代码块。其中,调用了静态方法Integer.compare()来比较两个整数的大小。

然后使用Lambda表达式实现自定义的函数式接口MyFunctionalInterface:创建一个实现该接口的对象,Lambda表达式表示了在run(int num1, int num2)方法中要执行的代码块。

运行结果:

1
Sum: 30

3.2.4 方法引用的简化形式

import java.util.function.Consumer;
import java.util.function.Supplier;

public class LambdaExample {
   
     
    public static void main(String[] args) {
   
     
        // 1. 对象::实例方法
        Consumer<String> consumer1 = System.out::println;
        consumer1.accept("Hello, World!");
        
        // 2. 类名::静态方法
        Supplier<Double> supplier = Math::random;
        double randomNum = supplier.get();
        System.out.println(randomNum);
       
    }
}

在上面代码中,我使用 对象::实例方法 来创建一个Consumer对象,使用对象引用方法的简化形式来执行accept()方法。
使用类名::静态方法 来创建一个Supplier对象,使用类名引用静态方法的简化形式来执行get()方法。

运行结果:

Hello, World!
0.6158534899118129

4️⃣ 应用场景

Lambda表达式在多种场景下都能发挥作用,包括但不限于:

  • 函数式接口(Functional Interface):通过Lambda表达式可以实现函数式接口的匿名内部类替代;
  • 集合操作:使用Lambda表达式可以在集合上进行筛选、映射、归约等操作,让代码更加简洁和易读;
  • 多线程编程:使用Lambda表达式可以方便地实现多线程任务的并行处理;
  • GUI事件处理:Lambda表达式可以简化GUI应用程序中的事件处理代码。

5️⃣ 优化技巧

在编写和使用Lambda表达式时,可以考虑以下优化技巧:

  • 避免过度复杂化:尽量保持Lambda表达式简洁,以提高代码可读性。
  • 使用方法引用:当Lambda表达式仅仅是调用一个已经存在的方法时,可以使用方法引用来代替,使得代码更加简洁。
  • 多线程优化:结合并行流(parallel stream)和Lambda表达式,可以实现多核处理器的并行计算,提高效率。

6️⃣ 原理

Java语言中的lambda表达式是一种闭包(Closure)的实现方式,它允许我们将函数作为参数传递给其他方法,并且能够在需要的时候延迟执行这些代码。

Lambda表达式最初在Java 8中引入,主要用于简化匿名内部类的编写,使得代码更加简洁、易读。Lambda表达式被视为一种可传递的代码块,充当了函数式接口的一个实例。

Lambda表达式的原理基于如下几个关键概念:

1、 ***函数式接口:Lambda表达式的类型由目标上下文推断得出,并且该上下文必须是函数式接口***函数式接口是只包含一个抽象方法的接口;
2、 Lambda表达式语法:Lambda表达式可以通过箭头符号(->)来定义,左侧表示输入参数,右侧表示方法体例如:(参数)->{方法体}
3、 类型推断:Java编译器使用目标上下文,根据参数类型进行类型推断,以确定Lambda表达式的类型;
4、 闭包与捕获变量:Lambda表达式可以访问其周围的局部变量和参数,甚至可以访问final或事实上是final的变量这些被访问的变量称为捕获变量,并且它们会在形成闭包时被复制;

当使用Lambda表达式时,编译器将根据语法规则将其转化为对应的函数式接口实例。在运行时,可以像传递常规对象一样传递和执行Lambda表达式。这种方式使得我们可以更方便地使用函数式编程风格来处理集合、并发等问题。

7️⃣ 注意性能问题

Java中的lambda表达式在某些情况下可能导致性能问题。以下是几种可能的情况:

1、 过多的自动装箱和拆箱:Lambda表达式中的参数和返回值类型是通过自动装箱和拆箱来实现的如果在频繁调用的场景中使用Lambda表达式,会产生大量的对象创建和销毁操作,从而引起性能问题;
2、 频繁创建匿名内部类:每次使用Lambda表达式时,都会创建一个匿名内部类的实例如果在循环或高并发应用中频繁创建大量的匿名内部类实例,会导致不必要的开销,影响性能;
3、 循环中的Lambda表达式:如果在循环中使用Lambda表达式,每次迭代都会创建一个新的Lambda实例对于大型循环,特别是多层嵌套的循环,这可能导致频繁的垃圾回收和额外的开销,降低性能;
4、 Lambda表达式的逻辑复杂臃肿:如果Lambda表达式内部包含大量的复杂逻辑操作,例如循环、条件判断等,可能会导致Lambda执行时的时间复杂度增加,从而降低性能;

为了避免上述问题,可以采取以下对应策略:

  • 避免在性能敏感的代码块中过度使用Lambda表达式,尤其是频繁调用的地方。
  • 尽量重复使用Lambda实例,避免频繁创建匿名内部类。
  • 对于循环内的Lambda表达式,可以考虑将其提取到循环外部,以减少实例的创建次数。
  • 简化并优化Lambda表达式的逻辑,减少复杂度和执行时间。

同时,最重要的是需要根据具体场景和性能需求评估使用Lambda表达式可能带来的性能影响,并进行必要的优化措施。

* 总结

Java Lambda表达式为Java 8引入了强大的功能,通过简洁的语法使开发者能够更高效地编写代码。无论是在集合操作、多线程编程还是GUI事件处理中,都可以灵活应用Lambda表达式。但需要注意的是,在某些情况下它们可能导致性能问题,因此需要谨慎使用。


[* ]nbsp_nbsp 1