【Java】深入浅出 Lambda 表达式,从入门到实操

哈喽各位小伙伴,近期一直在梳理 Java8 核心新特性内容,今天就来详细聊聊 Java8 标志性语法 ——Lambda 表达式。Lambda 是函数式编程落地 Java 的关键,用好它能大幅精简冗余代码,尤其是在接口回调、集合遍历、多线程场景中提升编码效率。

一、初识 Lambda 表达式

Lambda 是 Java8 新增的匿名方法语法,核心作用:把一段代码块当作方法参数进行传递,延迟执行逻辑,摆脱传统匿名内部类繁琐的格式书写。

基础语法格式

(参数列表) -> 方法体
  • ():存放方法入参,无参数时空括号;单个参数可省略括号
  • ->:Lambda 专属箭头标识符,分割参数和方法体
  • 方法体:单行代码可省略大括号与 return,多行必须用{}包裹

最简示例(无参 Lambda):

// 无参数,单行输出
() -> System.out.println("云扬");

二、结合 Runnable 接口理解 Lambda

Runnable是典型函数式接口,源码被@FunctionalInterface注解修饰,规定接口中仅有一个抽象方法,只有满足这个条件的接口,才支持 Lambda 实现。

1. Runnable 接口源码

@FunctionalInterface
public interface Runnable {
    void run();
}

@FunctionalInterface注解会在编译期校验接口抽象方法数量,避免误添加多个抽象方法导致 Lambda 无法使用。

2. Java8 之前:匿名内部类创建线程

传统写法冗余,需要重写完整接口方法,代码臃肿:

public class LambdaDemo {
    public static void main(String[] args) {
        // 匿名内部类写法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("传统匿名内部类启动线程");
            }
        }).start();
    }
}

3. Lambda 简化写法

借助 Lambda 省略接口实现类,一行搞定线程创建:

public class LambdaDemo {
    public static void main(String[] args) {
        // Lambda简化Runnable实现
        new Thread(() -> System.out.println("Lambda启动线程")).start();
    }
}

对比能直观感受到 Lambda 精简代码的优势,这也是日常开发中最常用的场景之一。

三、Lambda 四种常用使用场景

Lambda 不只是用来创建线程,日常开发中可用于变量赋值、返回值、数组元素、方法入参四种场景。

public class LambdaUse {
    public static void main(String[] args) {
        // 场景1:赋值给接口变量
        Runnable run = () -> {
            System.out.println("Lambda赋值变量");
        };
        run.run();

        // 场景2:作为方法入参(前面线程示例)
        new Thread(() -> System.out.println("作为方法参数")).start();

        // 场景3:Lambda作为数组元素
        Runnable[] arr = {
                () -> System.out.println("数组元素1"),
                () -> System.out.println("数组元素2")
        };
        arr[0].run();
    }

    // 场景4:方法返回Lambda表达式
    public static Runnable getRun() {
        return () -> System.out.println("Lambda作为返回值");
    }
}

四、Lambda 变量作用域细节

1. 外部变量限制:final/effectively final

Lambda不会开辟独立作用域,内部引用局部变量时,变量必须是final或者等效 final(只赋值一次,隐性 final),不允许直接在 Lambda 内修改基础类型局部变量。

错误示例:

public class ScopeTest {
    public static void main(String[] args) {
        int num = 10;
        Runnable r = () -> {
            // 编译报错:num非final,不能修改
            num++;
            System.out.println(num);
        };
    }
}

2. 三种突破变量修改限制的方案

实际开发中需要在 Lambda 修改数值,推荐三种解决方式:

  1. 静态 static 变量:静态变量存放在方法区,不受 final 约束
public class VarDemo {
    static int count = 0;
    public static void main(String[] args) {
        Runnable r = () -> {
            count++;
            System.out.println(count);
        };
        r.run();
    }
}
  1. AtomicInteger 原子类(推荐,多线程安全):JUC 并发包工具,原子自增
import java.util.concurrent.atomic.AtomicInteger;
public class VarDemo {
    public static void main(String[] args) {
        AtomicInteger count = new AtomicInteger(0);
        Runnable r = () -> {
            count.incrementAndGet();
            System.out.println(count.get());
        };
        r.run();
    }
}
  1. 数组包装:数组是引用类型,数组地址不变即可修改内部元素
public class VarDemo {
    public static void main(String[] args) {
        int[] arr = {0};
        Runnable r = () -> {
            arr[0]++;
            System.out.println(arr[0]);
        };
        r.run();
    }
}

五、Lambda 中 this 关键字

再次强调:Lambda 没有独立作用域,this指向定义 Lambda 的外部类实例,和匿名内部类完全不同(匿名内部类 this 指向自身内部类对象)。

public class ThisDemo {
    public void test() {
        // Lambda的this = ThisDemo类实例
        Runnable r = () -> System.out.println(this);
        r.run();
    }

    public static void main(String[] args) {
        new ThisDemo().test();
    }
}

六、文末小结

  1. Lambda 依托 函数式接口(@FunctionalInterface) 使用,单一抽象方法是前提;
  2. 核心作用:简化匿名内部类,实现函数式编程,代码轻量化;
  3. 变量规则:局部变量要求 effectively final,需要修改可用原子类、数组、静态变量;
  4. this 指向:外层宿主对象,无独立内部类 this。

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*