Java - Lambda

简介

介绍

Lambda表达式:在Java8语言中引入的一种新的语法元素和操作符。这个操作符为"->",该操作符被称为Lambda操作符或箭头操作符。它将 Lambda分为两个部分:

左侧:指定了Lambda表达式需要的参数列表

右侧:指定了Lambda体,是抽象方法的实现逻辑,也即Lambda表达式要执行的功能。

java 复制代码
ThreadFactory factory = r -> new Thread(r, "窗口" + count.getAndIncrement());

//等号左边的目标类型
ThreadFactory factory

//lambda 的三部分
r            ->        new Thread(r, "窗口" + count.getAndIncrement())
│            │         │
入参数列表    箭头      方法体(要执行的逻辑)/返回值

前置条件

Lambda 不能单独存在,必须赋值给一个函数式接口类型的变量、参数或返回值。

函数式接口:是指只有一个抽象方法的接口,通常用@FunctionalInterface 标注

抽象方法就是只有方法声明、没有方法体的方法,用 abstract 修饰,必须由子类(或实现类)去实现。接口中没有方法体的方法,隐式就是 public abstract。

java 复制代码
// 可以:Runnable 是函数式接口
Runnable r = () -> System.out.println("hi");

// 可以:ThreadFactory 是函数式接口
ThreadFactory f = task -> new Thread(task);

// 不可以:Object 不是函数式接口
Object o = () -> System.out.println("hi"); // 编译错误

参数

1、目标类型

目标类型决定了什么

目标类型(target type)就是 lambda 要被赋值或传递到的那个函数式接口类型。它决定了四件事:

决定项 说明 例子
实现哪个接口 lambda 最终实现的是目标接口 Runnable r = () -> {},实现 Runnable
实现哪个抽象方法 函数式接口只有一个抽象方法,lambda 就是实现它 Runnable.run()
参数类型和个数 由抽象方法签名决定 Runnable.run() 无参,所以 ();Comparator.compare(T,T) 两个参数
返回值类型 由抽象方法返回类型决定 Callable<Integer> 要求返回 Integer

不是固定的。 lambda 的写法有多种合法变体,同一逻辑也可以换不同方式实现。

2、入参

因为接口是函数式接口(只有一个抽象方法),Lambda 的入参类型就是那个唯一抽象方法的参数类型。编译器不需要猜,直接拿这个方法的签名来套。

示例代码

java 复制代码
//先定义一个函数式接口
@FunctionalInterface
interface Greeting {
    String sayHello(String name);
}

public class Demo {
    public static void main(String[] args) {
        Greeting greeting = name -> "你好, " + name;

        String message = greeting.sayHello("张三");
        System.out.println(message);

        // 也可以直接打印返回值
        System.out.println(greeting.sayHello("李四"));
    }
}

入参类型确认流程

java 复制代码
Greeting greeting = name -> System.out.println("你好, " + name);

//编译器看左边
Greeting greeting

//知道目标类型是 Greeting,而 Greeting 的方法是:
void sayHello(String name);
//所以推断 name 是 String

3、返回值

lambda 没有"返回参数"这个说法,只有返回值。返回值类型由函数式接口那个唯一抽象方法的返回类型决定。

java 复制代码
@FunctionalInterface
interface Greeting {
    String sayHello(String name);   // 返回类型是 String
}

//唯一抽象方法 String sayHello(String name) 决定了:
//    参数:name 是 String;
//    返回值:必须是 String。
//所以 lambda 右边的方法体必须能产生一个 String:

Greeting g = name -> "你好, " + name;   // 表达式结果是 String,匹配
Greeting g = name -> 123;   // 编译错误,int 不能作为 String 返回

返回值的三种情况

接口方法返回 void

java 复制代码
//接口方法返回 void
@FunctionalInterface
interface Task {
    void run();
}

//lambda 右边不需要产生返回值
Task t = () -> System.out.println("执行");

接口方法有返回值,lambda 是单表达式

java 复制代码
//接口方法有返回值,lambda 是单表达式
@FunctionalInterface
interface Calculator {
    int add(int a, int b);
}

Calculator c = (a, b) -> a + b;

接口方法有返回值,lambda 是多行代码块

java 复制代码
//接口方法有返回值,lambda 是多行代码块
@FunctionalInterface
interface Calculator {
    int add(int a, int b);
}

Calculator c = (a, b) -> {
    int sum = a + b;
    return sum;          // 必须显式 return
};

多线程示例

简单

1、创建线程;

2、设置线程名。

java 复制代码
ThreadFactory factory = r -> new Thread(r, "窗口" + count.getAndIncrement());//返回Thread

//等号左边的目标类型
ThreadFactory factory

//lambda 的三部分
r            ->       new Thread(r, "窗口" + count.getAndIncrement())
│            │         │
入参数列表    箭头      方法体(要执行的逻辑)/返回值

复杂

1、创建线程;

2、设置线程名。

3、设置未捕获异常处理器

java 复制代码
ThreadFactory factory = r -> {
    Thread t = new Thread(r, "窗口" + seq.getAndIncrement());
    
    //任务里抛出的异常如果没被 catch,最终会到这里,至少能打日志,不会被静默吞掉
    //给线程 t 设置"未捕获异常处理器。
    //给线程 t 设置"未捕获异常处理器
    t.setUncaughtExceptionHandler((thread, ex) ->System.err.println(thread.getName() + " 异常: " + ex));
    return t;//返回Thread
};

流程

java 复制代码
//创建一个线程安全的计数器,初始值为 1
AtomicInteger count = new AtomicInteger(1);

//只是定义,还没进行实际调用
//自定义线程工厂,让线程名仍然是"窗口1、窗口2、窗口3"
ThreadFactory factory = r -> new Thread(r, "窗口" + count.getAndIncrement());

//创建固定 3 个线程的线程池,并指定用上面的工厂创建线程
//r 仍然没有值
ExecutorService service = Executors.newFixedThreadPool(3, factory);

//把同一个 Runnable 对象提交 3 次,3 个工作线程共享 ticket
//多肽:实际调用Executors.newFixedThreadPool
service.execute(w);//这里 r 第一次被赋值
service.execute(w);
service.execute(w);

// 3. 关闭线程池
service.shutdown();

调用关系

java 复制代码
service.execute(w)                        // command = w
	  └─ Executors.newFixedThreadPool(3, factory)     // 多态调用
		  └─ ThreadPoolExecutor.execute(w)       // 实现类,不是接口
		       └─ addWorker(w, true)             // firstTask = w
		            ├─ Worker worker = new Worker(w)         // 把 w 包进 Worker
		            └─ threadFactory.newThread(worker)       // ★ 这里才调用 newThread
		                  └─ 你的 lambda:r = worker          // ★ r 在这里被赋值
java 复制代码
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  threadFactory);
}
相关推荐
xiaoye-duck17 分钟前
《C++ 项目:手写大模型接入 SDK》DataManager数据管理模块:SQLite持久化存储
开发语言·数据库·c++·sqlite
Evand J21 分钟前
【MATLAB例程,PMSM19】永磁同步电机(PMSM)基于多速率无迹卡尔曼滤波(UKF)的转速与转子位置估计。测量缺失、状态预测与恢复性能分析
开发语言·matlab·电机·ekf·卡尔曼·电机滤波
喵了几个咪29 分钟前
GoWind Flow 拆解:一个 Go 工作流引擎与它的双模式设计器
开发语言·人工智能·后端·golang
2601_967980111 小时前
win10如何把python加入环境变量
python·win10·安装教程·命令行·环境变量
꧁细听勿语情꧂1 小时前
二叉树余题,排序、直接插入排序、希尔排序
c语言·开发语言·数据结构·算法
是main不是漫1 小时前
【C++——类和对象(上)】筑基篇:以封装奠定面向对象之基
c语言·开发语言·c++
apcipot_rain5 小时前
C++转Java日记(06)
java·开发语言
锋行天下9 小时前
langgraph 后端发自定义数据,前端接收方法概括
人工智能·python
金銀銅鐵10 小时前
带您看懂字节码指令(一):一帧里有什么
java·后端·ai编程