jdk已经到24了,lambda大家在工作中或许或多或少都写过一些,本文将从最基础的语法开始,并逐渐到高级用法,相信不管是刚入门的新同学还是java十年老兵,总能从本文找到一部分有用的知识点;同理,博主一个人水平也有局限,非常欢迎大家评论补充更多知识点
文章目录
基础语法
无返回值
lamda函数可以简化方法名 ,例如
public void firstLambda() {
System.out.println("name");
}
可以简化为:
() -> {System.out.println("name");};
单行代码大括号可以省略, 进一步省略:
() -> System.out.println("name");
调用示例:
public class LambdaTestOne {
public static void main(String[] args) {
DoIt name = () -> System.out.println("name");
name.doSomething();
//等效
new LambdaTestOne().firstLambda();
}
public void firstLambda() {
System.out.println("name");
}
}
有返回值
public Integer returnValue(Integer a) {
return a + 1;
}
可以简化为:
(a) -> {return a + 1;};
当代码只有一行时,return和大括号都可以省略:
其中(a) 表示入参
(a) -> a + 1;
整体调用代码示例:
class LambdaTestTwo {
public static void main(String[] args) {
Function<Integer, Integer> integerCallable = (a) -> a + 1;
Integer res = integerCallable.apply(3);
System.out.println(res);
//等效
Integer r = new LambdaTestTwo().returnValue(3);
System.out.println(r);
}
public Integer returnValue(Integer a) {
return a + 1;
}
}
有参与无参
我们先看无入参:
Supplier <Integer> integer = () -> 1 + 1;
Integer res = integer.get();
System.out.println(res);
等效于执行了一个方法:
public Integer returnValue() {
return 1 + 1;
}
有入参:
Function<Integer, Integer> integerCallable = (a) -> a + 1;
Integer res = integerCallable.apply(3);
System.out.println(res);
入参只有一个时,()也可以省略
() -> a + 1;
特别注意:
因为lambda很灵活,灵活也会造成混乱问题,比如本来应该是其它函数类型 有时候也会用function来代替,就可能会出现初学者不太理解的地方,我们先看看源码注释:
Function是有两个泛型的,T表示入参类型,R表示返回值类型 , 所以用Function类型的lambda表达式,()->
这个括号里面必须有值,也就是说lambda函数必须是个有参函数
// ()-> 会编译报错
Function<Integer, Integer> error = () -> 1 + 1;
// 正确通过
Function<Integer, Integer> right = (a) -> 1 + 1;
这就相当于传统写法 虽然定义了一个入参,但我们方法体里面不用上它:
public Integer returnValue(Integer a) {
return 1 + 1;
}
四大常用函数:
- Function<T, R>:接受 T 返回 R
Function<String, Integer> strLen = s -> s.length();
System.out.println(strLen.apply("abc")); // 输出 3
- Predicate:接收 T 返回 boolean(常用于过滤、校验)
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // true
- Consumer:接收 T 无返回(消费型)
Consumer<String> sayHi = name -> System.out.println("Hi, " + name);
sayHi.accept("Tom");
- Supplier:无参返回 T(供给型)
Supplier<Double> random = () -> Math.random();
System.out.println(random.get());
结合集合操作(Stream + Lambda)
List<String> list = Arrays.asList("apple", "banana", "pear");
// 过滤长度大于 5 的字符串,并转大写
List<String> result = list.stream()
.filter(s -> s.length() > 5)
// map 此处只有一行代码 省略了return 关键字,这也是为什么有的时候我们能在map看到return 有的时候看不到
.map(s -> s.toUpperCase())
.collect(Collectors.toList());
System.out.println(result); // [BANANA]
stream流常用api
移步博主主页 搜索:stream流 ;有文章单独例举常用api
异步任务编排(CompletableFuture + Lambda)
示例:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class AsyncDemo {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
sleep(1);
return 10;
})
.thenApply(x -> x * 2)
.thenAccept(result -> System.out.println("最终结果:" + result));
System.out.println("主线程继续执行中...");
sleep(2); // 等待异步任务完成
}
private static void sleep(int sec) {
try {
TimeUnit.SECONDS.sleep(sec);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
CompletableFuture lambda api详解
我们知道CompletableFuture 有非常多的api,靠死记硬背是记不住的,所以我们要归类并理解
第一类:启动任务
// 无返回值(类似 Runnable)
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
System.out.println("任务1:执行中");
});
// 有返回值(类似 Supplier) supply的意思为供给 供给就是提供返回值
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "任务2:返回结果";
});
第二类:正常流程处理
- thenApply:有返回值
CompletableFuture<String> f = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World"); // 有返回值
- thenAccept:有输入但无返回值
CompletableFuture
.supplyAsync(() -> "Hello")
.thenAccept(System.out::println); // 打印:Hello
- thenRun:无输入、无返回值
CompletableFuture
.supplyAsync(() -> "计算完毕")
.thenRun(() -> System.out.println("任务完成")); // 不接收上一步结果
当然,方法也可以连用:
CompletableFuture.runAsync(() -> {
sleep(1);
// 这里返回2
}).thenApply(a -> 1 + 1 )
// 返回4
.thenApply(x -> x * 2)
// 打印输出4
.thenAccept(result -> System.out.println("最终结果:" + result));
第三类:异常处理
- exceptionally:异常时提供默认值
CompletableFuture<String> f = CompletableFuture
.supplyAsync(() -> {
if (true) {
throw new RuntimeException("异常了");
}
return "正常结果";
})
.exceptionally(ex -> "默认值");
String s = null;
try {
s = f.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 默认值
System.out.println(s);
- handle:处理结果或异常(最灵活)
CompletableFuture<String> f = CompletableFuture
.supplyAsync(() -> {
if (true) throw new RuntimeException("错误");
return "成功";
})
.handle((result, ex) -> {
if (ex != null) return "有异常:" + ex.getMessage();
return "正常结果:" + result;
});
String s = null;
try {
s = f.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 有异常:java.lang.RuntimeException: 错误
System.out.println(s);
第四类:任务组合
- thenCombine:两个任务完成后合并结果
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> result = f1.thenCombine(f2, (a, b) -> a + " " + b);
- thenCompose:嵌套任务展开(flatMap)
CompletableFuture<String> f = CompletableFuture
.supplyAsync(() -> "id")
.thenCompose(id -> CompletableFuture.supplyAsync(() -> "user_" + id));
实战口诀帮助记忆:
方法名 | 中文理解 | 关键特征 |
---|---|---|
thenApply | 然后转换(function.apply) | 有入参,有返回值 |
thenAccept | 然后消费(comsumer.accpet) | 有入参,无返回值 |
thenRun | 然后执行 | 无入参,无返回值 |
handle | 统一处理异常 | 有异常/正常都处理 |
exceptionally | 异常降级 | 只处理异常,返回默认值 |
thenCombine | 结果合并 | 合并两个返回值 |
thenCompose | 链式任务 | 嵌套任务变平铺 |
方法引用(语法更简洁)
List<String> names = Arrays.asList("Tom", "Jerry", "Lucy");
// 使用方法引用简化:s -> System.out.println(s)
names.forEach(System.out::println);
类型:
对象的实例方法:x -> x.method() → instance::method
静态方法引用:x -> Class.staticMethod(x) → Class::staticMethod
构造器引用:() -> new Class() → Class::new
instance 指对象实例,Class指类名
class LambdaTestTwo {
public static void main(String[] args) {
LambdaTestTwo lambdaTestTwo = new LambdaTestTwo();
Function<Integer, Integer> returnValue = lambdaTestTwo::returnValue;
// 如果是静态方法 则直接使用类名
//Function<Integer, Integer> staticMethod = LambdaTestTwo::staticMethod;
Integer apply = returnValue.apply(1);
System.out.println(apply); //2
}
public Integer returnValue(Integer a) {
return a + 1;
}
public static Integer staticMethod(Integer a) {
return a + 1;
}
}
Lambda 表达式注意事项
Lambda 表达式只能用于函数式接口 即只有一个抽象方法的接口(例如Function类 只有一个apply方法)。
Lambda 表达式中不能抛出未捕获的检查异常(需手动处理或包装)。
Lambda 表达式内访问的局部变量必须是 final 或 等价于 final
使用 BiFunction<T, U, R> 进行双参数运算
很多时候Function不能满足我们的需求,我们需要不同的参数传入,可以使用BiFunction , BiFunction能传入两个参数
(有的同学可能要问,那我实际不止两个参数,可能有多个呢?这时候可以参数合并,比如传入一个Pair 传入一个实体类 传入一个map都可以 就别只传一个String去占用参数位置了)
import java.util.function.BiFunction;
public class BiFunctionExample {
public static void main(String[] args) {
BiFunction<Double, Double, Double> calcTotalPrice = (price, shipping) -> price + shipping;
double result = calcTotalPrice.apply(299.99, 15.0);
System.out.println("总价: " + result); // 314.99
}
}
Supplier + Lazy 缓存(懒加载)
这是一个神技,可以防止一些入参时的空指针,可以减少不必要的IO交互或资源消耗(有实战经历的同学应该能明白在说什么)
import java.util.function.Supplier;
public class LazyCache<T> {
private final Supplier<T> supplier;
private T cache;
private boolean loaded = false;
public LazyCache(Supplier<T> supplier) {
this.supplier = supplier;
}
public T get() {
if (!loaded) {
cache = supplier.get();
loaded = true;
}
return cache;
}
// 清除缓存
public void reset() {
loaded = false;
cache = null;
}
// 示例
public static void main(String[] args) {
LazyCache<String> configCache = new LazyCache<>(() -> {
System.out.println("加载配置...");
return "配置内容";
});
System.out.println("第一次获取: " + configCache.get()); // 触发加载
System.out.println("第二次获取: " + configCache.get()); // 用缓存
configCache.reset();
System.out.println("重置后再次获取: " + configCache.get()); // 再次加载
}
}