JAVA 8 Consumer与BiConsumer

本文深入探讨Java 8中Consumer与BiConsumer接口的功能与应用,详细解析其源码,展示如何通过实例实现链式调用及异常处理。同时,文章提供了JDK内部对这两个接口的典型使用案例。

参考:
https://www.jianshu.com/p/63771441ba31
https://blog.youkuaiyun.com/qq_28410283/article/details/80704487
https://www.yiibai.com/geek/detail/518

1、Consumer

源码:

package java.util.function;

import java.util.Objects;

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 * 表示“接受一个参数输入且没有任何返回值的操作“。不同于其它的函数式接口,Consumer期望通过方法的实现来执行具体的操作。
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * 可实现方法,接受一个参数且没有返回值
     * 
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * 默认方法,提供链式调用方式执行。执行流程:先执行本身的accept在执行传入参数after.accept方法。
     * 该方法会抛出NullPointerException异常。
     * 如果在执行调用链时出现异常,会将异常传递给调用链功能的调用者,且发生异常后的after将不会在调用。
     * 
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

使用实例:

package jdk8;

import java.util.function.Consumer;

public class ConsumerTest {

    public static void main(String[] args) {
        testConsumer();
        testAndThen();
    }

    /**
     * 一个简单的平方计算
     */
    public static void testConsumer(){
        Consumer<Integer> square = x -> System.out.println("print square : " + x * x);
        square.accept(2);
    }

    /**
     * 定义3个Consumer并按顺序进行调用andThen方法,其中consumer2抛出NullPointerException。
     */
    public static void testAndThen(){
        Consumer<Integer> consumer1 = x -> System.out.println("first x : " + x);
        Consumer<Integer> consumer2 = x -> {
            System.out.println("second x : " + x);
            throw new NullPointerException("throw exception test");
        };
        Consumer<Integer> consumer3 = x -> System.out.println("third x : " + x);

        consumer1.andThen(consumer2).andThen(consumer3).accept(1);
    }
}

执行结果:

print square : 4

first x : 1
second x : 1
Exception in thread "main" java.lang.NullPointerException: throw exception test
    at jdk8.ConsumerTest.lambda$testAndThen$2(ConsumerTest.java:27)
    at java.util.function.Consumer.lambda$andThen$0(Consumer.java:65)
    at java.util.function.Consumer.lambda$andThen$0(Consumer.java:65)
    at jdk8.ConsumerTest.testAndThen(ConsumerTest.java:31)
    at jdk8.ConsumerTest.main(ConsumerTest.java:9)

Process finished with exit code 1

结论:

  1. 默认方法,提供链式调用方式执行。执行流程:先执行本身的accept再执行传入参数after.accept方法
  2. 依次执行andThen的Consumer的accept,直到遇到null异常,不再执行异常后的andThen。

jdk内对Consumer的典型使用:

在jdk内对Consumer的典型使用非foreach莫属了(在 java.lang.Iterable内),下面是源码:

/**
     * Performs the given action for each element of the {@code Iterable}
     * until all elements have been processed or the action throws an
     * exception.  Unless otherwise specified by the implementing class,
     * actions are performed in the order of iteration (if an iteration order
     * is specified).  Exceptions thrown by the action are relayed to the
     * caller.
     *
     * @implSpec
     * <p>The default implementation behaves as if:
     * <pre>{@code
     *     for (T t : this)
     *         action.accept(t);
     * }</pre>
     *
     * @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

2、BiConsumer

源码:

@FunctionalInterface
public interface BiConsumer<T, U> {
 
    
    void accept(T t, U u);
 
	/**本接口中的accept先执行,传入的BiConsumer 接口类型的参数,后执行accept*/
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);
 
        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }
}

使用实例:

跟Consumer一样,都有一个 accept方法,只不过,Consumer接受1个泛型参数而BiConsumer接受2个泛型参数

 Map<String, String> map = new HashMap<>();
        map.put("a", "a");
        map.put("b", "b");
        map.put("c", "c");
        map.put("d", "d");
        map.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });

Map接口的终端操作,forEach的参数就是BiConsumer函数接口,对HashMap 的数据进行消费;BiConsumer函数接口还有一个默认函数,andThen接收一个BiConsumer接口先执行本接口的再执行传入的参数

package com.yiibai.tutorial.lambda; 
import java.util.function.BiConsumer; 

public class BiConsumerExample2 {
	 public static void main(String[] args) { 
		 BiConsumer addition = (a b) -> { 
		 	System.out.println(a + b); 
		 }; 
		 BiConsumer subtraction = (a b) -> { 
		 	System.out.println(a - b); 
		 }; 
		 // Using andThen() 
		 addition.andThen(subtraction).accept(10 6); 
	 }
  }

执行结果:

16
4
Java 8 中,`Consumer` `BiConsumer` 是两个函数式接口,它们都用于对数据进行消费操作,但两者之间存在关键的区别,主要体现在参数的数量使用场景上。 ### 参数数量 `Consumer<T>` 接口定义了一个 `accept` 方法,该方法接受一个参数,用于对单个对象执行某些操作。接口定义如下: ```java @FunctionalInterface public interface Consumer<T> { void accept(T t); } ``` 相比之下,`BiConsumer<T, U>` 接口同样定义了一个 `accept` 方法,但它接受两个参数,允许对两个对象执行操作。其接口定义如下: ```java @FunctionalInterface public interface BiConsumer<T, U> { void accept(T t, U u); } ``` ### 使用场景 由于 `Consumer` 只能处理单一参数,它通常用于只需要一个输入值的情况。例如,打印一个对象的信息或者更新一个对象的状态。 ```java Consumer<Person> consumer = (p) -> System.out.println(p.getFirstName()); ``` 而 `BiConsumer` 因为其能够处理两个参数的特点,适用于需要同时处理两个输入值的情况。比如,同时更新两个不同对象的状态或者比较两个对象的属性。 ```java BiConsumer<String, String> biConsumer = (first, second) -> System.out.println(first + " " + second); ``` ### 组合操作 两者都提供了 `andThen` 方法来组合多个操作,但是 `BiConsumer` 的 `andThen` 方法需要传入另一个 `BiConsumer` 实例,而 `Consumer` 的 `andThen` 方法则接受另一个 `Consumer` 实例。这意味着 `BiConsumer` 的 `andThen` 方法可以链接多个接受两个参数的操作,而 `Consumer` 的 `andThen` 方法只能链接接受单一参数的操作 [^2]。 ```java // Consumer 使用 andThen Consumer<String> printName = name -> System.out.println(name); Consumer<String> printGreeting = name -> System.out.println("Hello, " + name); printName.andThen(printGreeting).accept("John"); // BiConsumer 使用 andThen BiConsumer<String, String> printNames = (name1, name2) -> System.out.println(name1 + " and " + name2); BiConsumer<String, String> greetNames = (name1, name2) -> System.out.println("Hello, " + name1 + " and " + name2); printNames.andThen(greetNames).accept("Alice", "Bob"); ``` ### 总结 总的来说,`Consumer` `BiConsumer` 的主要区别在于它们处理的参数数量不同,这直接影响了它们适用的场景。选择哪一个取决于具体的应用需求。如果只需要处理一个参数,则使用 `Consumer`;如果需要处理两个参数,则应该选择 `BiConsumer` [^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值