Lambda 表达式详解

1 初识 Lambda 表达式

Lambda 表达式是 Java 8 引入的一个重要特性,它描述了一个代码块(或称为匿名方法),可以将其作为参数传递给构造方法或普通方法以便后续执行。Lambda 表达式的基本语法如下:

java 复制代码
() -> System.out.println("沉默王二")
  • () 表示 Lambda 表达式的参数列表(本例中没有参数)。
  • -> 是 Lambda 表达式的标识符,表示这是一个 Lambda 表达式。
  • System.out.println("沉默王二") 是要执行的代码,即将"沉默王二"打印到标准输出流。

2 Lambda 表达式与 Runnable 接口

在 Java 中,Runnable 接口是多线程编程的基础接口,其定义如下:

java 复制代码
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Runnable 接口只有一个抽象方法 run(),并且使用了 @FunctionalInterface 注解。这个注解表示该接口是一个函数式接口,可以通过 Lambda 表达式创建其实例。

在 Java 8 之前,创建一个线程并启动它通常需要使用匿名内部类:

java 复制代码
public class LamadaTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("沉默王二");
            }
        }).start();
    }
}

而使用 Lambda 表达式,代码可以大大简化:

java 复制代码
public class LamadaTest {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("沉默王二")).start();
    }
}

Lambda 表达式不仅易于理解,还大大简化了代码量。

3 Lambda 表达式的语法

每个 Lambda 表达式都遵循以下语法规则:

java 复制代码
( parameter-list ) -> { expression-or-statements }
  • () 中的 parameter-list 是以逗号分隔的参数。你可以指定参数的类型,也可以不指定(编译器会根据上下文进行推断)。
  • -> 是 Lambda 表达式的标识符。
  • {} 中的 expression-or-statements 为 Lambda 的主体,可以是一行语句,也可以是多行语句。

Lambda 表达式可以用于多种场景,例如:

  1. 为变量赋值

    java 复制代码
    Runnable r = () -> { System.out.println("沉默王二"); };
    r.run();
  2. 作为返回结果

    java 复制代码
    static FileFilter getFilter(String ext) {
        return (pathname) -> pathname.toString().endsWith(ext);
    }
  3. 作为数组元素

    java 复制代码
    final PathMatcher matchers[] = {
        (path) -> path.toString().endsWith("txt"),
        (path) -> path.toString().endsWith("java")
    };
  4. 作为普通方法或构造方法的参数

    java 复制代码
    new Thread(() -> System.out.println("沉默王二")).start();

4 Lambda 表达式的作用域

Lambda 表达式并不会引入新的作用域,因此在 Lambda 表达式中使用的变量必须是 finaleffectively final。例如:

java 复制代码
public static void main(String[] args) {
    int limit = 10;
    Runnable r = () -> {
        int limit = 5; // 编译错误:变量 limit 已经定义过了
        for (int i = 0; i < limit; i++)
            System.out.println(i);
    };
}

上述代码会导致编译错误,因为 limit 变量在 Lambda 表达式中被重新定义。

5 解决 Lambda 表达式中的变量修改问题

如果需要在 Lambda 表达式中修改局部变量的值,可以考虑以下几种解决方案:

  1. 将变量声明为 static

    java 复制代码
    public class ModifyVariable2StaticInsideLambda {
        static int limit = 10;
        public static void main(String[] args) {
            Runnable r = () -> {
                limit = 5;
                for (int i = 0; i < limit; i++) {
                    System.out.println(i);
                }
            };
            new Thread(r).start();
        }
    }
  2. 使用 AtomicInteger

    java 复制代码
    public class ModifyVariable2AtomicInsideLambda {
        public static void main(String[] args) {
            final AtomicInteger limit = new AtomicInteger(10);
            Runnable r = () -> {
                limit.set(5);
                for (int i = 0; i < limit.get(); i++) {
                    System.out.println(i);
                }
            };
            new Thread(r).start();
        }
    }
  3. 使用数组

    java 复制代码
    public class ModifyVariable2ArrayInsideLambda {
        public static void main(String[] args) {
            final int[] limits = {10};
            Runnable r = () -> {
                limits[0] = 5;
                for (int i = 0; i < limits[0]; i++) {
                    System.out.println(i);
                }
            };
            new Thread(r).start();
        }
    }

6 Lambda 表达式与 this 关键字

Lambda 表达式并不会引入新的作用域,因此在 Lambda 表达式中使用的 this 关键字与外部类的 this 关键字相同。例如:

java 复制代码
public class LamadaTest {
    public static void main(String[] args) {
        new LamadaTest().work();
    }

    public void work() {
        System.out.printf("this = %s%n", this);

        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.printf("this = %s%n", this);
            }
        };
        new Thread(r).start();
        new Thread(() -> System.out.printf("this = %s%n", this)).start();
    }
}

Tips:%s 代表当前位置输出字符串,%n 代表换行符,也可以使用 \n 代替,但 %n 是跨平台的。

分析
work() 方法中的代码可以分为 3 个部分:

  • 单独的this关键字
java 复制代码
System.out.printf("this = %s%n", this);

其中 thismain() 方法中通过 new 关键字创建的 LamadaTest 对象------new LamadaTest()

  • 匿名内部类中的 this 关键字
java 复制代码
Runnable r = new Runnable()
{
    @Override
    public void run()
    {
        System.out.printf("this = %s%n", this);
    }
};

其中 thiswork() 方法中通过 new 关键字创建的 Runnable 对象------new Runnable(){...}

  • Lambda 表达式中的 this 关键字

其中this关键字和 1)中的相同。

输出结果如下

java 复制代码
this = com.cmower.java_demo.journal.LamadaTest@3feba861
this = com.cmower.java_demo.journal.LamadaTest$1@64f033cb
this = com.cmower.java_demo.journal.LamadaTest@3feba861

7 总结

Lambda 表达式极大地简化了 Java 编程,特别是在处理函数式接口时。然而,不当的使用可能会导致不必要的混乱,因此在使用 Lambda 表达式时需要谨慎。理解 Lambda 表达式的语法、作用域以及与 this 关键字的关系,将有助于更好地利用这一强大的特性。

8 思维导图

9 参考链接

深入浅出Java 8 Lambda表达式

相关推荐
hrrrrb1 分钟前
【Spring Security】Spring Security 密码编辑器
java·hive·spring
豐儀麟阁贵4 分钟前
2.3变量与常量
java·开发语言
摇滚侠1 小时前
Spring Boot 3零基础教程,自动配置机制,笔记07
spring boot·笔记·后端
兮动人1 小时前
Eureka注册中心通用写法和配置
java·云原生·eureka
JJjiangfz1 小时前
杭电 神经网络与深度学习 学习笔记
深度学习·神经网络·学习
什么半岛铁盒1 小时前
C++11 多线程与并发编程
c语言·开发语言·c++
爱编程的小白L3 小时前
基于springboot志愿服务管理系统设计与实现(附源码)
java·spring boot·后端
Kiri霧5 小时前
Linux下的Rust 与 C 的互操作性解析
c语言·开发语言·rust
聪明的笨猪猪5 小时前
Java Redis “持久化”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
雪芽蓝域zzs6 小时前
uniapp AES 加密解密
开发语言·uni-app·c#