四十五、函数式接口与 Lambda 表达式

😫 痛点引入 :匿名内部类写一堆 new Runnable() { @Override ... },三行代码只干一件事?

Lambda 表达式一句话搞定!配合四大函数式接口,Java 代码像写诗一样优雅!📘

今天从 Lambda 语法到 Consumer/Supplier/Function/Predicate,一篇让你的代码「瘦身」80%!


一、Lambda 表达式是什么?🤔

1.1 一句话理解

Lambda = 匿名内部类的简化版,把「new 接口(){ 重写方法 }」变成「()-> 方法体」

1.2 ⚠️ 使用前提:函数式接口

复制代码
✅ 能用 Lambda 的接口:只有一个抽象方法(函数式接口)
❌ 不能用 Lambda 的类/接口:普通类、多个抽象方法的接口
java 复制代码
// 匿名内部类(之前)
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("run方法体");
    }
};

// Lambda 表达式(之后)✨ 三行变一行!
Runnable r2 = () -> System.out.println("run方法体");

二、Lambda 语法格式 🏗️

2.1 基本结构

复制代码
(参数列表) -> { 方法体 }
   ↑       ↑       ↑
 参数们   箭头   要执行的代码

2.2 四种语法格式速查表

格式 写法 省略规则 适用场景
无参无返回值 ()->{ body } body 只有一句可省略 {} Runnable
有参无返回值 (x)->{ body } 一个参数可省略 () Consumer
多参无返回值 (x,y)->{ body } body 只有一句可省略 {} BiConsumer
有参有返回值 (x,y)->{ return x; } 只有 return 可省略 {}return Function

2.3 全格式代码示例

java 复制代码
public class Demo01 {
    public static void main(String[] args) {
        // 格式1:无参无返回值
        Inter1 i1 = () -> {
            System.out.println("马上该完事了!");
        };
        // 方法体只有一句,省略大括号
        Inter1 i1_short = () -> System.out.println("马上该完事了!");
        i1_short.test();

        // 格式2:有一个参数,无返回值
        Inter2 i2 = (x) -> System.out.println("你好" + x);
        // 只有一个参数,小括号也可以省略!
        Inter2 i2_short = x -> System.out.println("你好" + x);
        i2_short.sayHi("苏澳");

        // 格式3:多个参数,无返回值
        Inter3 i3 = (x, y) -> System.out.println(x + y);
        i3.printSum(10, 20);  // 输出 30

        // 格式4:有参有返回值(重点!⭐)
        // 多条语句 → 大括号不能省略
        Inter4 i4 = (x, y) -> {
            double pow = Math.pow(x, y);
            return pow;
        };
        // 只有一条 return 语句 → 省略大括号和 return,自动返回!
        Inter4 i4_short = (x, y) -> Math.pow(x, y);
        System.out.println(i4_short.getPow(2, 3));  // 8.0
    }
}

interface Inter1 { void test(); }
interface Inter2 { void sayHi(String name); }
interface Inter3 { void printSum(int a, int b); }
interface Inter4 { double getPow(double a, double b); }

2.4 💡 省略规则记忆口诀

复制代码
一句体 → 省大括号
一个参 → 省小括号
一句 return → 省大括号 + 省 return
多条语句 → 乖乖写全

三、函数式接口的校验 🔒

3.1 @FunctionalInterface 注解

java 复制代码
@FunctionalInterface  // ← 加上它,不是函数式接口就编译报错!
interface MyInterface {
    void test();  // ✅ 只有一个抽象方法

    // void test2();  // ❌ 再去掉注释看看?编译直接报错!
}

3.2 函数式接口的定义

条件 说明
只有一个抽象方法 可以有 default/static 方法,不算抽象
不加 @FunctionalInterface 也行 但加了编译器会帮你检查

四、Consumer<T> 消费型接口 💳

4.1 接口定义

复制代码
Consumer<T>:消费型接口
  📥 有输入(T 类型)
  📤 无返回(void)
  → 只进不出,类似"花钱消费"

4.2 核心方法

方法 说明
void accept(T t) 消费传入的数据,无返回值

4.3 代码示例:书源的按摩消费之路 💳

java 复制代码
import java.util.function.Consumer;

public class ConsumerDemo {
    public static void main(String[] args) {
        // 消费规则1:踢脸服务
        Consumer<Integer> con1 = m ->
            System.out.println("书源享受踢脸服务,花费" + m + "元");

        // 消费规则2:摸脸服务(升级版)
        Consumer<Integer> con2 = m ->
            System.out.println("书源到天上人间,享受苏澳摸脸服务,花费" + m + "元");

        // 书源开始消费!
        enjoyServer(10, con1);   // 10块踢脸
        enjoyServer(800, con2);  // 800块摸脸
    }

    // 通用消费方法:传入金额 + 消费规则
    public static void enjoyServer(int money, Consumer<Integer> consumer) {
        consumer.accept(money);  // 执行消费!
    }
}

运行结果

复制代码
书源享受踢脸服务,花费10元
书源到天上人间,享受苏澳摸脸服务,花费800元

4.4 💡 Consumer 的典型场景

  • list.forEach(x -> System.out.println(x)) → forEach 就接收 Consumer!
  • 遍历集合、处理数据、打印日志
  • 有入参,不需要返回值,就是 Consumer!

五、Supplier<T> 供给型接口 🏭

5.1 接口定义

复制代码
Supplier<T>:供给型接口
  📥 无输入
  📤 有返回(T 类型)
  → 只出不进,类似"工厂生产"

5.2 核心方法

方法 说明
T get() 无参,返回一个 T 类型结果

5.3 代码示例:苏澳的赚钱之路 🏭

java 复制代码
import java.util.function.Supplier;

public class SupplierDemo {
    public static void main(String[] args) {
        // 挣钱方案1:卖银首饰
        Supplier<Integer> s1 = () -> {
            System.out.println("苏澳去卖银首饰");
            return 3000;
        };

        // 挣钱方案2:踩缝纫机(包吃住)
        Supplier<Integer> s2 = () -> {
            System.out.println("苏澳踩缝纫机赚钱,每次1块,包吃住");
            return 5;
        };

        // 统一获取收入
        System.out.println("收入:" + getMoney(s1));
        System.out.println("收入:" + getMoney(s2));
    }

    public static int getMoney(Supplier<Integer> supplier) {
        return supplier.get();  // 从 Supplier 那里"拿"结果!
    }
}

5.4 💡 Supplier 的典型场景

  • Optional.ofNullable(x).orElseGet(() -> defaultValue) → 懒加载默认值
  • 工厂模式:根据规则生成对象
  • 无参有返回值,就是 Supplier!

六、Function<T,R> 函数型接口 🔄️

6.1 接口定义

复制代码
Function<T,R>:函数型接口
  📥 有输入(T 类型)
  📤 有返回(R 类型,可以和 T 不同类型)
  → 有进有出,数据转换器!

6.2 核心方法

方法 说明
R apply(T t) 传入 T,返回 R
andThen(Function after) 先执行自己,再执行 after,链式处理

6.3 代码示例:字符串处理链 🔄️

java 复制代码
import java.util.function.Function;

public class FunctionDemo {
    public static void main(String[] args) {
        // 第1步:字符串 → 整数
        Function<String, Integer> toInt = x -> Integer.parseInt(x);

        // 第2步:整数 → 平方
        Function<Integer, Integer> square = x -> x * x;

        // andThen:先 toInt,结果交给 square 再处理!⭐
        // "3" → 3 → 9
        Integer result = toInt.andThen(square).apply("3");
        System.out.println("3 的平方:" + result);  // 9
    }
}

💡 andThen 的作用

复制代码
toInt.andThen(square).apply("3")

等价于:
  Integer temp = toInt.apply("3");  // "3" → 3
  Integer result = square.apply(temp);  // 3 → 9

6.4 💡 Function 的典型场景

  • 数据转换:String → Integer、DTO → VO
  • Stream 中的 map(x -> x.getName()) → 就是 Function!
  • 有进有出 → Function ✅

七、Predicate<T> 断言型接口 ⚖️

7.1 接口定义

复制代码
Predicate<T>:断言型接口
  📥 有输入(T 类型)
  📤 返回 boolean(只能是 true/false)
  → 条件判断器!

7.2 核心方法

方法 说明
boolean test(T t) 判断传入的参数
and(Predicate other) 逻辑与(&&)
or(Predicate other) 逻辑或(||)
negate() 取反(!)

7.3 代码示例:多条件组合判断 ⚖️

java 复制代码
import java.util.function.Predicate;

public class PredicateDemo {
    public static void main(String[] args) {
        // 判断1:字符串是否以 www 开头
        Predicate<String> startsWithWww = x -> x.startsWith("www");

        // 判断2:字符串是否以 xyz 结尾
        Predicate<String> endsWithXyz = x -> x.endsWith("xyz");

        // and:两个条件都满足 ✅
        System.out.println(startsWithWww.and(endsWithXyz)
            .test("www.baosy.xyz"));  // true

        // or:满足任意一个 ✅
        Predicate<Integer> isZero = x -> x == 0;
        Predicate<Integer> isOne = x -> x == 1;
        System.out.println(isZero.or(isOne).test(1));  // true(是1)

        // negate:取反 ✅
        Predicate<Integer> isPositive = x -> x > 0;
        System.out.println(isPositive.negate().test(3));  // false(3>0,取反)
    }
}

7.4 💡 Predicate 的典型场景

  • 校验参数:x -> x != nullx -> x > 0
  • Stream 中的 filter(x -> x > 10) → 就是 Predicate!
  • 能组合 and/or/negate,写校验规则极其优雅 ✅

八、四大函数式接口速查表 📋

接口 输入 输出 核心方法 场景
Consumer<T> 💳 T void accept(T) 遍历、消费数据
Supplier<T> 🏭 T get() 工厂、懒加载
Function<T,R> 🔄️ T R apply(T) / andThen 数据转换
Predicate<T> ⚖️ T boolean test(T) / and/or/negate 条件判断

九、方法引用------比 Lambda 更简洁 ✨

9.1 什么时候用方法引用?

复制代码
Lambda 里面的内容,别人已经实现过了?
  → 直接用方法引用!不用重复写!

9.2 引用格式

格式 语法 示例
引用静态方法 类名 :: 静态方法名 Math::pow
引用成员方法 对象名 :: 方法名 System.out::println
引用构造方法 类名 :: new Person::new

9.3 代码示例

java 复制代码
public class MethodRefDemo {
    public static void main(String[] args) {
        // Lambda 写法
        Inter6 i1 = x -> System.out.println(x);

        // 方法引用(更简洁!✨)
        Inter6 i2 = System.out::println;
        i2.print("张三");  // 输出:张三

        // Lambda:调用 Math.pow
        Inter7 i3 = (x, y) -> Math.pow(x, y);

        // 方法引用:直接引用 Math 的 pow 方法!
        Inter7 i4 = Math::pow;
        System.out.println(i4.getPow(10, 2));  // 100.0
    }
}

interface Inter6 { void print(String name); }
interface Inter7 { double getPow(int a, int b); }

9.4 💡 方法引用的本质

复制代码
任意一个方法 → 都可以当作「函数式接口的实现类对象」
方法引用 = 把方法当成数据来传递!

本篇总结 📝

  1. Lambda 表达式 🤔:匿名内部类的简化,前提是函数式接口
  2. 四种语法格式 🏗️:无参无反/有参无反/多参无返/有参有返(口诀助记)
  3. 函数式接口校验 🔒:@FunctionalInterface 注解,编译器帮你检查
  4. Consumer<T> 💳:accept(T) 只进不出,消费数据
  5. Supplier<T> 🏭:get() 只出不进,生产数据
  6. Function<T,R> 🔄️:apply(T) + andThen() 数据转换链
  7. Predicate<T> ⚖️:test(T) + and/or/negate() 组合判断
  8. 方法引用 ✨:类::方法 比 Lambda 更简洁,方法当数据传递!

🚀 下一篇预告:《四十六、Stream 流------集合操作的终极利器》------ 搞定 filter/map/reduce/collect,用流式思维优雅处理数据!


作者 :书源丶
发布平台:CSDN

相关推荐
直奔標竿1 小时前
MySQL与Redis数据一致性实战方案(避坑指南)
java·数据库·spring boot·redis·mysql·spring·缓存
java1234_小锋1 小时前
Java进程突然挂了如何排查?
java·开发语言
夕除1 小时前
spring boot--04
java·spring boot
java小白小1 小时前
Guava Cache 本地缓存
java
梦梦代码精1 小时前
LikeShop 二次开发扩展能力白皮书——面向业务增长的可扩展电商架构实践
java·架构·github
极客先躯1 小时前
高级java每日一道面试题-2025年12月05日-实战篇[Dockerj]-Docker 安装后的默认存储路径是什么?如何修改?
java·docker·默认存储路径在不同系统上的区别·linux overlay2·修改存储路径的理论方法·修改流程中的关键理论点
祁_z2 小时前
LangSmith 实操指南「Agent 可观测性系统」
java·服务器
小雅痞2 小时前
[Java][Leetcode hard] 76. 最小覆盖子串
java·算法·leetcode
admiraldeworm2 小时前
c -> true 导致异常返回 404 问题排查
c语言·开发语言