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表达式

相关推荐
路在脚下@1 分钟前
Spring如何处理循环依赖
java·后端·spring
数据小爬虫@1 分钟前
如何利用PHP爬虫获取速卖通(AliExpress)商品评论
开发语言·爬虫·php
jbjhzstsl11 分钟前
lv_ffmpeg学习及播放rtsp
学习·ffmpeg
青い月の魔女19 分钟前
数据结构初阶---二叉树
c语言·数据结构·笔记·学习·算法
网络安全(king)20 分钟前
网络安全攻防学习平台 - 基础关
网络·学习·web安全
一个不秃头的 程序员24 分钟前
代码加入SFTP JAVA ---(小白篇3)
java·python·github
qq_5895681027 分钟前
node.js web框架koa的使用
笔记·信息可视化·echarts
丁总学Java35 分钟前
--spring.profiles.active=prod
java·spring
上等猿43 分钟前
集合stream
java
java1234_小锋1 小时前
MyBatis如何处理延迟加载?
java·开发语言