函数编程-Consumer 和 BiConsumer


在这里插入图片描述

0、Consumer 用法总结

Consumer 适用于这样的场景,Consumer方法内部进行通用逻辑的处理,然后由匿名函数决定通用逻辑之后的流程,可以和策略模式相结合。需要注意的是,匿名函数是没有返回值的。

public static void main(String[] args) {
        int i = 0;
        if (i == 0) {
            consumerTest((param) -> {
                System.out.println(param);
            }, "a");
        } else if (i == 1) {
            consumerTest((param) -> {
                System.out.println(param);
            }, "b");
        }
    }
  • Consumer 作为函数的一个入参,Consumer< T>一般结合泛型
  • 包含以Consumer作为参数的函数在被调用时,Consumer部分将被一个匿名函数取代
  • 匿名函数是一段代码{},内部内容没有限制
  • 以 Consumer 为一个参数的函数,内部每调用一次 Consumer.accept(Object),匿名函数内容就被立即执行一次,如果没有被调用,则匿名函数的内容不会执行
  • Consumer.accept(Object) 中的Object 会被传递给匿名函数,一般形式为 param->{//匿名函数的逻辑}
  • 可以以多个Consumer作为入参,function.andThen(function2).accept(b+“哈哈哈”);
  • 匿名函数内部可以调用 以Consumer 为入参的函数,调用时,同样遵循上面的逻辑——即 【以 Consumer 为一个参数的函数,内部每调用一次 Consumer.accept(Object),匿名函数内容就被立即执行一次,如果没有被调用,则匿名函数的内容不会执行】。比如,调用函数1,函数1内部调用函数2,然后accept,.函数2内循环调用accept,运行顺序就是:函数1运行-调用函数2-函数2accept-函数2匿名函数-函数1accept-函数1匿名函数;函数2accept-函数2匿名函数-函数1accept-函数1匿名函数。

1、案例-简单(accept结果传入匿名函数)

import com.google.common.collect.Lists;
import org.apache.commons.collections4.ListUtils;

import java.util.List;

import java.util.function.Consumer;

/**
 * @Title:Consumer举例子
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: wujie
 * @Version: v1.0
 * @Updatedby:
 */
public class Del {
    public static void main(String[] args) {
    	//比如这里 param 是自定义的,表示 consumer accept 的结果
        consumerBuildParam(param->function(param),"a","b");
    }
    public static void function(String str){
        System.out.println(str);
    }

     /**
     * Consumer 的 accept 拼装出一个参数
     * @param function 任意函数,将把 consumer 的 accept 作为入参
     * @param a
     * @param b
     */
    public static void consumerBuildParam(Consumer<String> function,
                     String a,
                     String b){
        //function.accept 每调用一次,function 函数就会运行一次,当然,你也可以使用循环
        function.accept(a+b);
        function.accept(a+b);
    }
}

2、案例-多个 Consumer(一次 accept结果 传入多个 匿名函数)


import java.util.function.Consumer;

/**
 * @Title:Consumer举例子
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: wujie
 * @Version: v1.0
 * @Updatedby:
 */
public class Del {
    public static void main(String[] args) {
        consumerBuildParam(param->function(param),param2->function2(param2),"a");
    }
    public static void function(String str){
        System.out.println("f1"+str);
    }

    public static void function2(String str){
        System.out.println("f2"+str);
    }

    /**
     * function.accept 的结果作为参数,依次传入 function1 和 function2
     * function.accept 调动一次,则函数分别调用一次
     * @param function
     * @param function2
     * @param b
     */
    public static void consumerBuildParam(Consumer<String> function,
                                          Consumer<String> function2,
                                          String b){
        //function.accept 每调用一次,function 函数就会运行一次
        function.andThen(function2).accept(b+"哈哈哈");
        function.andThen(function2).accept(b+"哈哈哈");
    }


}

3、forEach 函数 对 Consumer 的封装[forEach(function) 与 forEach(item->function.accept(item)) 等效]

一般而言,我们需要写 consumer.accept 这一句,但是 forEach 函数提供了多态方法,支持我们直接把 consumer 对象传进去,在阅读代码时可以留意一下

  • forEach(function) 与 forEach(item->function.accept(item)) 等效
  • 代码
import com.google.common.collect.Lists;

import java.util.function.Consumer;

/**
 * @Title:Consumer举例子
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: wujie
 * @Version: v1.0
 * @Updatedby:
 */
public class Del {
    public static void main(String[] args) {
        consumerBuildParam(param->function(param));
    }
    public static void function(String str){ System.out.println("f1"+str);
    }

    /**
     * function.accept 的结果作为参数,依次传入 function1 和 function2
     * function.accept 调动一次,则函数分别调用一次
     * @param function
     */
    public static void consumerBuildParam(Consumer<String> function){
        //forEach 封装了 action.accept
        Lists.newArrayList("aaa","bbb","ccc","ddd").stream().forEach(function);
        Lists.newArrayList("aaa","bbb","ccc","ddd").stream().forEach(item->function.accept(item));

    }


}

  • 结果
f1aaa
f1bbb
f1ccc
f1ddd
f1aaa
f1bbb
f1ccc
f1ddd

4、Consumer 的嵌套使用

只要 调用一次 accept,就会立即执行本方法调用处的匿名函数。
本方法执行多次accept,就执行多次本方法调用处的匿名函数。
匿名函数内部执行了含Consumer的函数,则等匿名函数执行完毕,像是一个栈。

4.1、Consumer 匿名方法调用另一个含Consumer作为入参的函数

  • 代码
import java.util.function.Consumer;

/**
 * @Title:Consumer举例子
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: bestcxx
 * @Version: v1.0
 * @Updatedby:
 */
public class Del {
    public static void main(String[] args) {
        Del del = new Del();
        del.function(param1 -> {
            System.out.println("第一次匿名函数 param1="+param1);
            del.function2(param2->{
                System.out.println("第二次匿名函数,param1="+param1+";param2="+param2);
            });
        });
    }

    public void function(Consumer<Integer> inner) {
        System.out.println("Del.function");
        for(int i=0;i<2;i++){
            System.out.println("循环 Del.function.accept");
            inner.accept(i);
        }
    }
    public void function2(Consumer<Integer> inner) {
        System.out.println("Del.function2");
        for(int i=0;i<2;i++){
            System.out.println("循环 Del.function2.accept");
            inner.accept(i);
        }
    }

}

  • 结果
    Del.function
    循环 Del.function.accept
    第一次匿名函数 param1=0
    Del.function2
    循环 Del.function2.accept
    第二次匿名函数,param1=0;param2=0
    循环 Del.function2.accept
    第二次匿名函数,param1=0;param2=1
    循环 Del.function.accept
    第一次匿名函数 param1=1
    Del.function2
    循环 Del.function2.accept
    第二次匿名函数,param1=1;param2=0
    循环 Del.function2.accept
    第二次匿名函数,param1=1;param2=1

4.2、子类拓展父类 Consumer &循环(实战场景)

子类函数调用父类函数(super),父类函数 accept 传递给子类函数内的匿名函数,子类accept,传递给子类的匿名函数,在子类中实现了逻辑拓展
父类内部有循环 accept,则立即执行子类匿名函数,子类函数循环 accept,理解循环执行子类匿名函数,从外到内开始,从内到外扩展结束。

  • 父类代码
import java.util.function.Consumer;

/**
 * @Title:Consumer举例子
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: bestcxx
 * @Version: v1.0
 * @Updatedby:
 */
public class Del {

    public void function(Consumer<Integer> inner) {
        System.out.println("Del.function");
        for(int i=0;i<2;i++){
            System.out.println("循环 Del.function.accept");
            inner.accept(i);
        }
    }

}
  • 子类代码和main方法
import java.util.function.Consumer;

/**
 * @Title:Consumer举例子
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: bestcxx
 * @Updatedby:
 */
public class SubDel extends Del {

    public static void main(String[] args) {
        SubDel sub = new SubDel();
        sub.function(param2->{
            System.out.println("SubDel.main 匿名函数 param2="+param2);
        });
    }

    @Override
    public void function(Consumer<Integer> inner) {
        System.out.println("SubDel.function");
        super.function(innerParam->{
            System.out.println("SubDel.function 匿名函数:param1="+innerParam +" SubDel.function.accept");
            inner.accept(1);
        });
    }
}

  • 结果
    SubDel.function
    Del.function
    循环 Del.function.accept
    SubDel.function 匿名函数:param1=0 SubDel.function.accept
    SubDel.main 匿名函数 param2=1
    循环 Del.function.accept
    SubDel.function 匿名函数:param1=1 SubDel.function.accept
    SubDel.main 匿名函数 param2=1

5、BiConsumer 可以构造两个参数

Consumer 每次调用 accept可以构造一个参数,然后传入到匿名函数中,
BiConsumer 每次调用 accept可以构造两个参数,然后传入到匿名函数中。
在具体使用上,大同小异。

5.1、BiConsumer 含 accept 用法举例

  • 代码
import java.util.function.BiConsumer;

/**
 * @Title:BiConsumer 测试BiConsumer
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: wujie
 * @Updatedby:
 */
public class BiConsumerDel {
    public static void main(String[] args) {
        BiConsumerDel biConsumerDel = new BiConsumerDel();
        biConsumerDel.testBiConsumer((t,u)->{
                    System.out.println("匿名函数逻辑");
            biConsumerDel.testMethod(t,u);
        }
        );
    }

    public void testMethod(String a,Integer b){
        System.out.println(String.format("a=%s,b=%d",a,b));
    }

    public void testBiConsumer(BiConsumer<String,Integer> biConsumer){
        biConsumer.accept("aaa",111);
        System.out.println("等");
        biConsumer.accept("aaa",222);
    }
}
  • 结果
匿名函数逻辑
a=aaa,b=111
等
匿名函数逻辑
a=aaa,b=222

5.1、BiConsumer 结合 foreach 免去 accept 用法举例

Map 的 foreach 正好有俩参数,提供了静态方法,其内部封装了 accept 方法,调用时可以简写

  • 代码
import com.google.common.collect.Maps;

import java.util.Map;
import java.util.function.BiConsumer;

/**
 * @Title:BiConsumer 测试BiConsumer
 * @Description:
 * @Copyright: Copyright(c)2022
 * @Author: wujie
 * @Updatedby:
 */
public class BiConsumerDel {
    public static void main(String[] args) {
        BiConsumerDel biConsumerDel = new BiConsumerDel();
        biConsumerDel.testBiConsumer((t,u)->{
                    System.out.println("匿名函数逻辑");
            biConsumerDel.testMethod(t,u);
        }
        );
    }

    public void testMethod(String a,Integer b){
        System.out.println(String.format("a=%s,b=%d",a,b));
    }

    public void testBiConsumer(BiConsumer<String,Integer> biConsumer){
        Map<String,Integer> map = Maps.newHashMap();
        map.put("a1",1);
        map.put("a2",2);
        map.forEach(biConsumer);
    }
}
  • 结果
匿名函数逻辑
a=a1,b=1
匿名函数逻辑
a=a2,b=2

6、单独使用 Consumer\BiConsumer

这种方式,不必将Consumer\BiConsumer声明到具体名称的函数,而是可以直接在方法体中声明一个匿名函数。匿名函数的内容依旧在 accept 方法被调用时立即执行。

public static void main(String[] args) {
        BiConsumer<String,String> biConsumer = (A,B)->{
            System.out.println("我是匿名函数"+A+B);
        };
        Consumer<String> consumer = (C)->{
            System.out.println("我是匿名函数"+C);
        };
        biConsumer.accept("a","b");
        consumer.accept("c");
    }

我是匿名函数ab
我是匿名函数c

参考

函数式接口 Consumer

<think>好的,我现在需要回答用户关于Java中FunctionalInterfaceConsumerSupplier的用法与区别的问题。首先,我应该回忆这两个接口的基本定义用途。Consumer接口我记得是接受一个参数但不返回结果,通常用于消费数据,比如打印或者修改对象状态。而Supplier接口则是不接受参数但返回一个结果,用于提供数据,比如生成对象或获取配置。接下来,我需要确认它们的函数方法名。Consumer的方法是accept,而Supplier的是get。可能用户还会想知道如何在实际代码中使用它们,比如结合Lambda表达式或方法引用。 然后,区别方面,除了参数返回值的不同,还应该提到它们的不同应用场景。Consumer更多用于处理数据流中的元素,而Supplier用于延迟计算或生成值。此外,是否需要异常处理也是一个点,比如Supplier可能抛出异常,而Consumer通常不处理异常。 用户可能还想看代码示例,所以需要准备简单的例子,比如使用Consumer遍历列表,用Supplier生成随机数。同时,可以提到Java8中的Stream API中的应用,比如forEach使用Consumer,collect使用Supplier。另外,可能需要比较其他函数式接口,比如FunctionPredicate,但用户主要问的是ConsumerSupplier,所以重点放在这两个上。最后,生成相关问题,帮助用户进一步学习,比如其他函数式接口的用法,或者性能方面的考虑。需要确保使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$,但这里可能不太需要数学公式,所以主要注意代码块引用。引用之前提供的示例中的引用标记,但可能用户提供的引用内容不太相关,所以需要合理处理。 最后,检查回答结构是否清晰,是否有遗漏的重要点,并确保语言准确易懂,符合中文表达习惯。</think>在Java函数编程中,`Consumer``Supplier`是两个核心函数式接口,它们的差异主要体现在数据流方向上。以下是详细对比使用指南: ### 一、Consumer接口 #### 1. 定义与特性 - 函数签名:`void accept(T t)` - 数据流向:**单向输入**型接口 - 典型场景:数据消费(如打印、存储、状态修改) - 注解要求:必须标注`@FunctionalInterface` #### 2. 代码示例 ```java // Lambda表达式实现 Consumer<String> printer = s -> System.out.println(s.toUpperCase()); printer.accept("hello"); // 输出"HELLO" // 方法引用实现 List<Integer> numbers = Arrays.asList(1,2,3); numbers.forEach(System.out::println); // 流式操作消费数据 ``` #### 3. 派生接口 - `BiConsumer<T,U>`:双参数消费 - `IntConsumer`:专用于基本类型int ### 二、Supplier接口 #### 1. 定义与特性 - 函数签名:`T get()` - 数据流向:**无输入单输出**型接口 - 典型场景:延迟计算、对象工厂、配置获取 - 线程安全:建议实现为无状态 #### 2. 代码示例 ```java // 生成随机数 Supplier<Double> randomSupplier = Math::random; System.out.println(randomSupplier.get()); // 工厂模式应用 Supplier<LocalDateTime> timeFactory = LocalDateTime::now; System.out.println(timeFactory.get()); ``` ### 三、核心差异对比 | 特性 | Consumer | Supplier | |--------------------|--------------------------|--------------------------| | 函数方法 | `accept(T)` | `T get()` | | 参数/返回值 | 有输入无输出 | 无输入有输出 | | 异常处理 | 通常不抛出检查异常 | 可包装检查异常 | | 流式应用位置 | `forEach()`终端操作 | `collect()`的容器工厂 | | 组合方法 | `andThen()`链式组合 | 无组合方法 | ### 四、设计模式应用 1. **观察者模式**:通过`Consumer`实现事件监听 ```java List<Consumer<String>> listeners = new ArrayList<>(); listeners.add(s -> saveToDB(s)); listeners.add(s -> sendNotification(s)); // 触发事件 listeners.forEach(c -> c.accept("事件数据")); ``` 2. **工厂模式**:通过`Supplier`实现对象创建 ```java Map<String, Supplier<Shape>> shapeFactories = new HashMap<>(); shapeFactories.put("circle", Circle::new); shapeFactories.put("square", Square::new); Shape circle = shapeFactories.get("circle").get(); ``` ### 五、性能注意事项 1. **对象创建开销**:频繁调用`Supplier.get()`时,考虑对象池技术 2. **闭包捕获成本**:Lambda表达式捕获外部变量会生成合成类 3. **方法引用优化**:`Class::method`比Lambda更易被JVM内联 ### 六、扩展应用场景 - **Consumer链**:使用`andThen`组合多个操作 ```java Consumer<String> pipeline = ((Consumer<String>)s -> validate(s)) .andThen(s -> encrypt(s)) .andThen(s -> transmit(s)); ``` - **Supplier延迟计算**:配合`Optional`处理空值 ```java Optional.ofNullable(config) .orElseGet(() -> loadDefaultConfig()); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值