别翻了,Lambda 表达式入门,看这篇就够了(1)

简介: 别翻了,Lambda 表达式入门,看这篇就够了

今天分享的主题是《Lambda 表达式入门》,这也是之前一些读者留言强烈要求我写一写的,不好意思,让你们久等了,现在来满足你们,为时不晚吧?



01、初识 Lambda


Lambda 表达式描述了一个代码块(或者叫匿名方法),可以将其作为参数传递给构造方法或者普通方法以便后续执行。考虑下面这段代码:


() -> System.out.println("沉默王二")

1

来从左到右解释一下,() 为 Lambda 表达式的参数列表(本例中没有参数),-> 标识这串代码为 Lambda 表达式(也就是说,看到 -> 就知道这是 Lambda),System.out.println("沉默王二") 为要执行的代码,即将“沉默王二”打印到标准输出流。


有点 Java 基础的同学应该不会对 Runnable 接口感到陌生,这是多线程的一个基础接口,它的定义如下:


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



Runnable 接口非常简单,仅有一个抽象方法 run();细心的同学会发现一个陌生的注解 @FunctionalInterface,这个注解是什么意思呢?


我看了它的源码,里面有这样一段注释:


Note that instances of functional interfaces can be created with lambda expressions, method references, or constructor references.

大致的意思就是说,通过 @FunctionalInterface 标记的接口可以通过 Lambda 表达式创建实例。具体怎么表现呢?


原来我们创建一个线程并启动它是这样的:


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


通过 Lambda 表达式呢?只需要下面这样:


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


是不是很妙!比起匿名内部类,Lambda 表达式不仅易于理解,更大大简化了必须编写的代码数量。




02、Lambda 语法


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


( parameter-list ) -> { expression-or-statements }


() 中的 parameter-list 是以逗号分隔的参数。你可以指定参数的类型,也可以不指定(编译器会根据上下文进行推断)。Java 11 后,还可以使用 var 关键字作为参数类型,有点 JavaScript 的味道。


-> 相当于 Lambda 的标识符,就好像见到圣旨就见到了皇上。


{} 中的 expression-or-statements 为 Lambda 的主体,可以是一行语句,也可以多行。


可以通过 Lambda 表达式干很多事情,比如说


1)为变量赋值,示例如下:


Runnable r = () -> { System.out.println("沉默王二"); };

r.run();



2)作为 return 结果,示例如下:


static FileFilter getFilter(String ext)

{

   return (pathname) -> pathname.toString().endsWith(ext);

}



3)作为数组元素,示例如下:


final PathMatcher matchers[] =
{
        (path) -> path.toString().endsWith("txt"),
        (path) -> path.toString().endsWith("java")
};



4)作为普通方法或者构造方法的参数,示例如下:


new Thread(() -> System.out.println("沉默王二")).start();

1

需要注意 Lambda 表达式的作用域范围。


public static void main(String[] args) {
    int limit = 10;
    Runnable r = () -> {
        int limit = 5;
        for (int i = 0; i < limit; i++)
            System.out.println(i);
    };
}



上面这段代码在编译的时候会提示错误:变量 limit 已经定义过了。


和匿名内部类一样,不要在 Lambda 表达式主体内对方法内的局部变量进行修改,否则编译也不会通过:Lambda 表达式中使用的变量必须是 final 的。

aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8xMTc5Mzg5LWY4Y2E2NDE1NmMzNTAzZDgucG5n.png


这个问题发生的原因是因为 Java 规范中是这样规定的:


Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression

must either be declared final or be effectively final (§4.12.4),

or a compile-time error occurs where the use is attempted.

大致的意思就是说,Lambda 表达式中要用到的,但又未在 Lambda 表达式中声明的变量,必须声明为 final 或者是 effectively final,否则就会出现编译错误。


关于 final 和 effectively final 的区别,可能有些小伙伴不太清楚,这里多说两句。


final int a;
a = 1;
// a = 2;
// 由于 a 是 final 的,所以不能被重新赋值
int b;
b = 1;
// b 此后再未更改
// b 就是 effectively final
int c;
c = 1;
// c 先被赋值为 1,随后又被重新赋值为 2
c = 2;
// c 就不是 effectively final


明白了 final 和 effectively final 的区别后,我们了解到,如果把 limit 定义为 final,那就无法在 Lambda 表达式中修改变量的值。那有什么好的解决办法呢?既能让编译器不发出警告,又能修改变量的值。


思前想后,试来试去,我终于找到了 3 个可行的解决方案:


1)把 limit 变量声明为 static。


2)把 limit 变量声明为 AtomicInteger。


3)使用数组。


相关文章
|
3月前
|
Python
lambda函数简介
lambda函数简介
37 0
|
5月前
|
Java
·Lambda的基本使用介绍
这篇文章详细介绍了Java中Lambda表达式的基本概念和使用方法,包括Lambda的基本语法、如何使用Lambda表达式实现接口方法、函数引用以及构造方法的引用,并通过实例代码展示了Lambda表达式的简洁性和实用性。
·Lambda的基本使用介绍
|
8月前
|
C++
【C/C++ 编程 入门篇】 if条件判断表达式的使用教程
https://developer.aliyun.com/article/1465430
152 0
|
8月前
|
Java
Lambda 表达式复习
Lambda 表达式复习
36 1
Python知识点:lambda 表达式
lambda 表达可以被看做是一种匿名函数。它可以让你快速定义一个极度简单的单行函数。譬如这样一个实现三个数相加的函数:
|
缓存 Python
全部内置函数详细认识(上篇)
全部内置函数详细认识(上篇)
186 0
|
Java 开发者
学习笔记|运算符简介
快速学习运算符简介
100 0
学习笔记|运算符简介
|
编译器 C++
【c ++ primer 笔记】第4章 表达式
**运算对象转换** - `小整数类型(如bool、char、short等)通常会被提升成较大的整数类型,主要是 int 型` - 一般在进行二元运算符时,即使俩个数类型不一样,只要他们可以相互准换为同一个类型就可以。 - 运算符作用于类类型的运算对象时,用户可以自行定义其含义。这种做法称之为`重载运算符`
129 0
|
Java 编译器
深入理解Java Lambda 表达式
Java 8 的 Lambda 表达式已经不再是“新特性”。 现在很多人工作中会使用 Lambda 表达式。 但是,你是否真正理解 Lambda 表达式的底层原理?
283 0
深入理解Java  Lambda 表达式
|
算法
​LeetCode刷题实战439:三元表达式解析器
算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
181 0