【java】lambda表达式 呕心沥血讲解 由浅入深详细教程

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;
    }

四大常用函数:

  1. Function<T, R>:接受 T 返回 R
Function<String, Integer> strLen = s -> s.length();
System.out.println(strLen.apply("abc")); // 输出 3
  1. Predicate:接收 T 返回 boolean(常用于过滤、校验)
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // true
  1. Consumer:接收 T 无返回(消费型)
Consumer<String> sayHi = name -> System.out.println("Hi, " + name);
sayHi.accept("Tom");
  1. 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:返回结果";
});

第二类:正常流程处理

  1. thenApply:有返回值
CompletableFuture<String> f = CompletableFuture
    .supplyAsync(() -> "Hello")
    .thenApply(s -> s + " World"); // 有返回值

  1. thenAccept:有输入但无返回值
CompletableFuture
    .supplyAsync(() -> "Hello")
    .thenAccept(System.out::println); // 打印:Hello

  1. 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));

第三类:异常处理

  1. 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);
  1. 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);

第四类:任务组合

  1. thenCombine:两个任务完成后合并结果
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "World");

CompletableFuture<String> result = f1.thenCombine(f2, (a, b) -> a + " " + b);
  1. 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()); // 再次加载
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟秋与你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值