简介
介绍
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);
}