likes
comments
collection
share

带你学会Java的Supplier和Consumer接口的使用

作者站长头像
站长
· 阅读数 8

一、背景

1、简介

在Java 8中,随着函数式接口(Functional Interface)的引入,我们迎来了一系列新的编程范式和工具。其中,SupplierConsumer是两个非常基础且常用的函数式接口。对于初学者来说,理解并学会使用它们,对于提升编程能力和代码质量有着不可忽视的作用。

Java 8引入了函数式接口的概念后,使得Java编程更加灵活和简洁。其中,SupplierConsumer是Java 8中两个非常基础且常用的函数式接口。它们分别代表了数据的提供者和消费者,为我们在编程中处理数据提供了极大的便利。

下面,我用通俗易懂的语言,结合丰富的应用场景和案例,来讲解这两个接口。

2、生活故事

想象一下,我们有一个神奇的餐厅,叫做“函数式餐厅”。在这个餐厅里,有两个特别的角色:Supplier大厨和Consumer食客。

Supplier大厨是个非常勤劳的家伙,他整天在厨房里忙碌,准备各种美味佳肴。但他有一个特点,就是只负责做菜,不负责送到你的桌子上。他总是默默地站在厨房的某个角落,手里端着一盘盘诱人的菜肴,等着有人来取。

Consumer食客呢,他是个特别挑剔的家伙。他来到餐厅,不会直接点菜,而是告诉服务员他想吃什么类型的菜。服务员就会去找Supplier大厨,让大厨准备这道菜。当大厨做好菜后,Consumer食客就会走过来,品尝大厨的手艺,然后给出他的评价。

在这个餐厅里,SupplierConsumer是两个非常重要的角色。没有Supplier大厨,就没有美味的菜肴;没有Consumer食客,大厨的手艺就无法得到认可和赞赏。

其实,在计算机编程的世界里,SupplierConsumer也是两个非常重要的概念。Supplier就像那个默默在厨房里准备菜肴的大厨,它负责提供数据或者结果,但不关心这些数据或结果是如何被使用的。而Consumer就像那个挑剔的食客,它负责接收数据或结果,并对它们进行处理或消费。

所以,当你听到SupplierConsumer这两个词时,就可以想象它们在“函数式餐厅”里忙碌的身影,一个负责制作,一个负责品尝。这样,是不是觉得它们变得亲切又有趣了呢?

正文开始

二、Supplier接口

Supplier接口是一个函数式接口,它表示一个不接受任何参数,但能够产生某种类型结果的操作。你可以把它想象成一个生产东西的公司,你不需要给它提供任何原材料,它就能生产出你想要的东西。Supplier接口是一个没有参数的函数式接口,它只有一个get方法,用于返回某种类型的数据。你可以将Supplier想象成一个生产数据的公司,每次调用get方法时,它就会生产并返回一个新的数据。

1. Supplier接口的定义

Supplier接口在Java 8中定义如下:

package java.util.function;

/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

Supplier接口中只有一个无参的get()方法,它返回泛型类型T的结果。

2. Supplier接口的应用场景

场景一:随机数生成

假设我们需要生成一个指定范围的随机数,可以使用Supplier来封装随机数生成的逻辑。

import java.util.Random;  
import java.util.function.Supplier;  
  
public class RandomNumberSupplier {  
    public static void main(String[] args) {  
        Supplier<Integer> randomNumberSupplier = () -> new Random().nextInt(100);  
        System.out.println(randomNumberSupplier.get()); // 输出一个0到99之间的随机数  
    }  
}

场景二:创建对象

在创建对象时,我们可以使用Supplier来封装对象的创建逻辑,这样可以使代码更加清晰和易于维护。

import java.util.function.Supplier;  
  
public class User {  
    private String name;  
    private int age;  
  
    public User(String name, int age) {  
        this.name = name;  
        this.age = age;  
    }  
  
    @Override  
    public String toString() {  
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';  
    }  
  
    public static void main(String[] args) {  
        Supplier<User> userSupplier = () -> new User("张三", 25);  
        User user = userSupplier.get();  
        System.out.println(user); // 输出:User{name='张三', age=25}  
    }  
}

场景三:结合Stream API使用

Supplier可以与Java 8的Stream API结合使用,用于生成Stream的数据源。

import java.util.function.Supplier;  
import java.util.stream.Stream;  
  
public class StreamWithSupplier {  
    public static void main(String[] args) {  
        Supplier<Integer> numberSupplier = () -> (int) (Math.random() * 100);  
        Stream<Integer> numberStream = Stream.generate(numberSupplier);  
        numberStream.limit(5).forEach(System.out::println); // 输出5个随机数  
    }  
}

三、Consumer接口

Consumer接口也是一个函数式接口,它表示一个接受单一输入参数并且不返回任何结果的操作。你可以把它想象成一个消费者,你给它一个东西,它消费掉这个东西,但不给你任何回报。

1. Consumer接口的定义

Consumer接口在Java 8中定义如下:

/**
 * 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.
 *
 * <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);

}

Consumer接口中定义了一个accept方法,它接受一个泛型类型T的参数,并且没有返回值(void)。此外,它还提供了一个默认方法andThen,允许将多个Consumer串联起来,形成一个操作链。

2. Consumer接口的应用场景

场景一:打印数据

当我们需要打印某个数据时,可以使用Consumer来封装打印的逻辑。

import java.util.function.Consumer;  
  
public class PrintConsumer {  
    public static void main(String[] args) {  
        Consumer<String> printConsumer = System.out::println;  
        printConsumer.accept("Hello, World!"); // 输出:Hello, World!  
    }  
}

场景二:数据验证

在处理数据时,我们可能需要验证数据的合法性。使用Consumer可以方便地封装验证逻辑。

import java.util.function.Consumer;  
  
public class ValidationConsumer {  
    public static void main(String[] args) {  
        String data = "12345";  
        Consumer<String> validationConsumer = s -> {  
            if (s.length() < 5) {  
                throw new IllegalArgumentException("数据长度不足");  
            }  
            // 其他验证逻辑...  
        };  
        try {  
            validationConsumer.accept(data);  
            System.out.println("数据验证通过");  
        } catch (IllegalArgumentException e) {  
            System.out.println("数据验证失败:" + e.getMessage());  
        }  
    }  
}

**场景三:**修改集合元素

对于集合中的每个元素,我们可能需要执行一些修改操作。使用Consumer可以方便地对集合中的元素进行处理。

import java.util.ArrayList;  
import java.util.List;  
import java.util.function.Consumer;  
  
public class ListModificationWithConsumer {  
    public static void main(String[] args) {  
        List<Integer> numbers = new ArrayList<>();  
        numbers.add(1);  
        numbers.add(2);  
        numbers.add(3);  
          
        Consumer<Integer> multiplyByTwo = n -> n *= 2;  
          
        numbers.forEach(multiplyByTwo);  
          
        numbers.forEach(System.out::println); // 输出:2, 4, 6  
    }  
}

在这个例子中,我们定义了一个Consumer,它接受一个整数并将其乘以2。然后,我们使用forEach方法将这个操作应用于列表中的每个元素。

四、Supplier接口与Consumer接口与匿名内部类

在Java中,SupplierConsumer和匿名内部类都是编程工具,它们可以帮助我们更简洁、灵活地编写代码。

首先,我们来回顾一下这三个概念:

  • 匿名内部类:是一个没有名字的内部类,通常用于实现一个接口或继承一个类,而不需要单独定义一个类。它常常用于临时实现某个接口或扩展某个类的功能。
  • Supplier:是一个函数式接口,它有一个方法get(),用于提供(或者说生成)数据。你可以把Supplier想象成一个“数据工厂”,它负责按需生成数据。
  • Consumer:也是一个函数式接口,它有一个方法accept(),用于消费(或者说处理)数据。你可以把Consumer想象成一个“数据处理器”,它负责接收数据并执行某种操作。

现在,让我们用一个例子来说明它们之间的关系:

假设我们有一个简单的需求:从某个数据源获取一个整数,并将这个整数打印出来。

使用匿名内部类的方式,我们可以这样实现:

// 使用匿名内部类实现Supplier和Consumer  
import java.util.function.Consumer;  
import java.util.function.Supplier;  
  
public class ExampleWithAnonymousClass {  
    public static void main(String[] args) {  
        // 使用匿名内部类实现Supplier  
        Supplier<Integer> numberSupplier = new Supplier<Integer>() {  
            @Override  
            public Integer get() {  
                // 这里模拟从数据源获取一个整数  
                return 42;  
            }  
        };  
  
        // 使用匿名内部类实现Consumer  
        Consumer<Integer> numberConsumer = new Consumer<Integer>() {  
            @Override  
            public void accept(Integer value) {  
                // 这里模拟打印整数  
                System.out.println("The number is: " + value);  
            }  
        };  
  
        // 使用Supplier获取数据,并使用Consumer处理数据  
        Integer number = numberSupplier.get();  
        numberConsumer.accept(number);  
    }  
}

在这个例子中,我们分别使用匿名内部类实现了SupplierConsumer接口。numberSupplier负责提供数据(这里是硬编码的42),而numberConsumer负责处理数据(这里是打印出来)。

然而,随着Java 8的推出,我们有了更简洁的方式来实现同样的功能,那就是使用Lambda表达式:

import java.util.function.Consumer;  
import java.util.function.Supplier;  
  
public class ExampleWithLambda {  
    public static void main(String[] args) {  
        // 使用Lambda表达式实现Supplier  
        Supplier<Integer> numberSupplier = () -> 42;  
  
        // 使用Lambda表达式实现Consumer  
        Consumer<Integer> numberConsumer = value -> System.out.println("The number is: " + value);  
  
        // 使用Supplier获取数据,并使用Consumer处理数据  
        Integer number = numberSupplier.get();  
        numberConsumer.accept(number);  
    }  
}

在这个例子中,我们使用了Lambda表达式来替换匿名内部类的实现。Lambda表达式让代码更简洁、更易于阅读。现在,我们不再需要写那么多冗余的代码来定义接口的实现,只需要一行代码就可以完成。

总结起来,SupplierConsumer是函数式接口,它们提供了定义数据和数据处理逻辑的框架。而匿名内部类是Java中一种实现接口或继承类的手段,但在Java 8及以后的版本中,Lambda表达式通常是一种更简洁、更现代的实现方式。通过将这三者结合起来使用,我们可以编写出更加灵活和可维护的代码。

五、电商系统的应用举例

接下来,我通过电商系统中应用SupplierConsumer接口的实际案例,旨在帮助您更好地理解和记忆这两个接口在业务场景中的应用。

案例一:生成随机优惠券码(Supplier应用)

在电商系统中,经常需要生成随机优惠券码以吸引用户。我们可以使用Supplier接口来定义一个生成优惠券码的逻辑。

import java.util.UUID;  
import java.util.function.Supplier;  
  
public class CouponCodeGenerator {  
    private static final int COUPON_CODE_LENGTH = 10;  
  
    public static void main(String[] args) {  
        // 创建Supplier接口的实现,用于生成优惠券码  
        Supplier<String> couponCodeSupplier = () -> {  
            // 使用UUID生成随机字符串,并截取指定长度  
            String randomString = UUID.randomUUID().toString().substring(0, COUPON_CODE_LENGTH);  
            // 可能还需要一些额外的逻辑,比如去除连续的相同字符等  
            return randomString.toUpperCase();  
        };  
  
        // 使用Supplier生成优惠券码  
        String couponCode = couponCodeSupplier.get();  
        System.out.println("Generated coupon code: " + couponCode);  
    }  
}

案例二:商品库存减少(Consumer应用)

当用户在电商系统中购买商品时,我们需要减少相应商品的库存。可以使用Consumer接口来定义库存减少的逻辑。

import java.util.function.Consumer;  
  
public class ProductStockManager {  
    private int stock;  
  
    public ProductStockManager(int initialStock) {  
        this.stock = initialStock;  
    }  
  
    public void reduceStock(int quantity) {  
        // 创建Consumer接口的实现,用于减少库存  
        Consumer<Integer> stockReducer = (amount) -> {  
            if (amount <= stock) {  
                stock -= amount;  
                System.out.println("Stock reduced by " + amount + ". Remaining stock: " + stock);  
            } else {  
                System.out.println("Insufficient stock to reduce by " + amount);  
            }  
        };  
  
        // 使用Consumer减少库存  
        stockReducer.accept(quantity);  
    }  
  
    public static void main(String[] args) {  
        ProductStockManager manager = new ProductStockManager(100);  
        manager.reduceStock(5); // 假设用户购买了5件商品  
    }  
}

案例三:动态获取商品价格(Supplier应用)

商品的价格可能会根据促销活动、库存量等因素动态变化。我们可以使用Supplier接口来定义获取商品价格的逻辑。有点时候可能我们的商城系统有不同的价格对象,那么此时可以通过该接口返回不同对象的结果,也是OK的.

import java.util.function.Supplier;  
  
public class ProductPriceProvider {  
    private double price;  
  
    public ProductPriceProvider(double initialPrice) {  
        this.price = initialPrice;  
    }  
  
    public double getPrice() {  
        // 创建Supplier接口的实现,用于提供商品价格  
        Supplier<Double> priceSupplier = () -> {  
            // 这里可以加入一些逻辑,比如根据库存量或促销情况动态计算价格  
            return price;  
        };  
  
        // 使用Supplier获取价格  
        return priceSupplier.get();  
    }  
  
    public void setPrice(double newPrice) {  
        this.price = newPrice;  
    }  
  
    public static void main(String[] args) {  
        ProductPriceProvider provider = new ProductPriceProvider(100.0);  
        System.out.println("Current price: " + provider.getPrice());  
          
        // 假设在某个促销活动中,价格变为80元  
        provider.setPrice(80.0);  
        System.out.println("New price after promotion: " + provider.getPrice());  
    }  
}

案例四:处理用户评论(Consumer应用)

用户购买商品后,可能会留下评论。我们需要对评论进行处理,比如保存到数据库或进行情感分析。可以使用Consumer接口来定义处理评论的逻辑。

import java.util.function.Consumer;  
  
public class UserCommentProcessor {  
    public static void main(String[] args) {  
        // 假设这是用户留下的评论  
        String comment = "I love this product! It's amazing!";  
  
        // 创建Consumer接口的实现,用于处理用户评论  
        Consumer<String> commentProcessor = (userComment) -> {  
            // 这里可以加入保存评论到数据库的逻辑  
            System.out.println("Comment saved: " + userComment);  
            // 可能还需要进行情感分析或其他处理  
        };  
  
        // 使用Consumer处理用户评论  
        commentProcessor.accept(comment);  
    }  
}

通过这4个案例,大家是否感觉到一个接口和返回值有关,一个接口和循环有关,如果是,恭喜你应该有点意识了。

六、Supplier接口与Consumer接口的参数化传递

当涉及到参数化传递的场景时,SupplierConsumer接口可以提供一种灵活的方式来处理数据和执行操作。案例如下:

案例一:使用Supplier进行参数化数据获取

import java.util.function.Supplier;  
  
public class ParameterizedSupplierExample {  
  
    // 方法一:调用方法二,并传递一个Supplier作为参数  
    public static void methodOne() {  
        // 创建一个Supplier,用于提供数据  
        Supplier<String> dataSupplier = () -> "Data provided by Supplier";  
  
        // 调用方法二,并将Supplier作为参数传递  
        methodTwo(dataSupplier);  
    }  
  
    // 方法二:接受一个Supplier作为参数,并获取数据  
    public static void methodTwo(Supplier<String> dataSupplier) {  
        // 使用Supplier获取数据  
        String data = dataSupplier.get();  
  
        // 执行一些操作,比如打印数据  
        System.out.println("Data obtained from Supplier: " + data);  
  
        // 继续执行方法二的其他逻辑  
        // ...  
    }  
  
    public static void main(String[] args) {  
        methodOne(); // 调用方法一,触发整个流程  
    }  
}

案例二:使用Consumer进行参数化操作执行

import java.util.function.Consumer;  
  
public class ParameterizedConsumerExample {  
  
    // 方法一:调用方法二,并传递一个Consumer作为参数  
    public static void methodOne() {  
        // 创建一个Consumer,用于执行操作  
        Consumer<String> operationConsumer = message -> {  
            System.out.println("Executing operation on message: " + message);  
            // 这里可以添加更多的操作逻辑  
        };  
  
        // 调用方法二,并将Consumer作为参数传递  
        methodTwo("Hello, World!", operationConsumer);  
    }  
  
    // 方法二:接受一个字符串和一个Consumer作为参数,并执行操作  
    public static void methodTwo(String message, Consumer<String> operationConsumer) {  
        // 执行一些操作,比如打印消息  
        System.out.println("Processing message: " + message);  
  
        // 使用Consumer执行参数化操作  
        operationConsumer.accept(message);  
  
        // 继续执行方法二的其他逻辑  
        // ...  
    }  
  
    public static void main(String[] args) {  
        methodOne(); // 调用方法一,触发整个流程  
    }  
}

在第一个例子中,methodOne创建了一个Supplier,它用于提供数据,并将其作为参数传递给methodTwomethodTwo通过调用get方法从Supplier中获取数据,并执行后续操作。

在第二个例子中,methodOne创建了一个Consumer,它定义了要在消息上执行的操作,并将它连同消息一起传递给methodTwomethodTwo首先执行一些处理逻辑,然后调用accept方法,通过Consumer执行参数化操作。

这两个例子展示了如何使用SupplierConsumer作为参数化传递的接口,在方法间进行数据和操作的传递。这种模式允许更灵活和可重用的代码结构。

对于参数化传递,也可以看下这篇文章:blog.csdn.net/qq\_3660207…

通过这个参数化的传递,有没有感觉到一些设计模式的影子呢?

七、Supplier接口与Consumer接口与设计模式

SupplierConsumer接口与设计模式之间还有点关系。设计模式是解决在软件设计中经常遇到的一类问题的最佳实践。SupplierConsumer接口是Java 8引入的函数式接口,它们经常在设计模式中得到应用,使得代码更加简洁、灵活和可重用。

以下是几个设计模式中利用SupplierConsumer接口的例子:

策略模式与Supplier

策略模式定义了一系列可以互相替换的算法,使得算法可以独立于使用它的客户端变化。当需要将不同的行为作为参数传递给方法时,可以使用Supplier作为策略的表示。对于一些轻量级的设计模式的代码,用这个感觉也听方便的。

import java.util.function.Supplier;  
  
public class StrategyPatternWithSupplier {  
  
    public static void executeStrategy(Supplier<String> strategy) {  
        String result = strategy.get(); // 执行策略  
        System.out.println("Strategy result: " + result);  
    }  
  
    public static void main(String[] args) {  
        // 创建不同的策略  
        Supplier<String> strategyOne = () -> "Strategy One Executed";  
        Supplier<String> strategyTwo = () -> "Strategy Two Executed";  
  
        // 执行策略  
        executeStrategy(strategyOne);  
        executeStrategy(strategyTwo);  
    }  
}

消费者模式与Consumer

消费者模式通常与Consumer接口一起使用,表示一种行为,它接受数据并对其进行处理,但不返回任何结果。这种模式在函数式编程和流式处理中很常见。

import java.util.Arrays;  
import java.util.List;  
import java.util.function.Consumer;  
  
public class ConsumerPatternExample {  
  
    public static void processData(List<String> data, Consumer<String> consumer) {  
        data.forEach(consumer::accept); // 对每个数据项应用消费者行为  
    }  
  
    public static void main(String[] args) {  
        List<String> dataList = Arrays.asList("A", "B", "C");  
  
        // 创建消费者行为  
        Consumer<String> printer = message -> System.out.println(message);  
  
        // 处理数据,应用消费者行为  
        processData(dataList, printer);  
    }  
}

在这个例子中,processData方法接受一个数据列表和一个Consumer,并使用forEach遍历列表,对每个元素应用Consumer的行为(在这里是打印元素)。

适配器模式与Supplier和Consumer

适配器模式允许将一个类的接口转换成客户端所期望的另一种接口,从而使得原本不兼容的类可以一起工作。虽然适配器模式本身不直接涉及SupplierConsumer,但可以使用它们来增强适配器的功能。

例如,假设有一个旧的API返回String,而新的客户端期望使用Supplier<String>。适配器可以将旧的API适配为新的接口:

public class OldApi {  
    public String getData() {  
        return "Data from Old API";  
    }  
}  
  
public class Adapter implements Supplier<String> {  
    private final OldApi oldApi;  
  
    public Adapter(OldApi oldApi) {  
        this.oldApi = oldApi;  
    }  
  
    @Override  
    public String get() {  
        return oldApi.getData();  
    }  
}

现在客户端可以使用Supplier接口而不是直接调用旧API:

public class Client {  
    public static void main(String[] args) {  
        OldApi oldApi = new OldApi();  
        Supplier<String> adapter = new Adapter(oldApi);  
  
        String data = adapter.get(); // 使用适配器获取数据  
        System.out.println(data);  
    }  
}

这些例子展示了SupplierConsumer如何在设计模式中发挥作用,帮助实现更加灵活和模块化的代码结构。通过利用这些函数式接口,我们可以更容易地传递行为和数据,从而简化代码并提高可重用性。

八、Supplier接口与Consumer接口的对比

描述Supplier接口用于提供生成一个值或对象,而Consumer接口用于接收一个输入参数并执行某种操作,但不返回任何结果。

输入参数Supplier接口不接受任何输入参数,而Consumer接口接受一个输入参数。

返回值Supplier接口返回一个结果,而Consumer接口没有返回值(返回void)。

应用场景Supplier常用于需要延迟计算或生成值的场景,比如当你不希望立即计算某个值,而是在需要时才进行计算。而Consumer则常用于处理数据,例如遍历集合中的元素并对每个元素执行某些操作。

在实际编程中,我们可以根据具体需求选择使用这两个接口。例如,当我们需要生成一个随机数或获取某个资源时,可以使用Supplier;而当我们需要遍历集合并对每个元素执行某种操作时,可以使用Consumer。通过结合使用这两个接口,我们可以编写出更加灵活和可维护的代码。

九、学习方法

有没有感觉自己和很多人一样,就是学完了这个知识点,但是一直没在实际的项目中用到这两个接口,过了一阵子就忘记了,这该怎么办,怎么学习和上面这两个接口,能够让咱们彻底的掌握他们,试试如下方法:

理解核心概念

首先,确保你真正理解了SupplierConsumer的核心概念。Supplier是一个提供数据的接口,它不接收任何参数,并返回一个结果;而Consumer是一个接收数据并对其进行处理的接口,它不返回任何结果。理解这两个接口的基本作用和使用场景是掌握它们的第一步。

编写小例子

尝试编写一些简单的小例子来应用这两个接口。例如,你可以创建一个Supplier来生成随机数,或者创建一个Consumer来打印字符串。通过编写这些例子,你可以更好地理解它们的用法和如何在实际代码中应用它们。

参与实际项目

如果有机会,尝试在实际项目中应用SupplierConsumer。找到一些可以使用这两个接口的场景,并将它们融入到你的代码中。这样,你不仅可以在实践中巩固知识,还可以看到它们如何与其他代码和组件相互作用。

创造自己的场景

如果没有实际项目可用,你也可以尝试创造一些自己的场景来练习使用SupplierConsumer。例如,你可以编写一个简单的计算器程序,使用Supplier来提供操作数,使用Consumer来处理计算结果。

参考优秀代码

查看一些优秀的开源项目或代码库,看看其他人是如何使用SupplierConsumer的。这可以帮助你了解它们在实际应用中的最佳实践和常见用法。

持续复习和练习

定期回顾和复习你学过的知识是非常重要的。你可以创建一些复习笔记或练习题目,以便在需要时快速回顾和巩固。此外,参加编程社区或论坛的讨论,与其他人分享你的经验和问题,也是一个很好的学习方式。

关联实际应用

尝试将SupplierConsumer与你熟悉的日常场景或问题联系起来。例如,你可以想象一个自助餐厅的场景,其中Supplier是食物供应台,负责提供食物;而Consumer是顾客,负责取走并享用食物。通过这种联想,你可以更容易地记住和理解它们的作用。

最后,记住学习是一个持续的过程,不要期望一蹴而就。通过不断地实践、复习和应用,你会逐渐掌握并熟练运用SupplierConsumer这两个接口。

十、最后

其实很多开源已经用到了这两个接口,只是日常大家没有去关注而已,那如何发现呢? 

其实很简单,你只需要在IDEA开发工具中,去看下Consumer接口和Supplier有哪些子类,就可以了,找到了第三方包中的涉及到这些子类的地方,你就会发现早已融入了我们工作中,同时这两个接口也是一种软件工程中的扩展点的设计,对于一些增强类型的逻辑处理,也可以这么设计,比如在RestTemplate中,有这样一个类:RequestFactoryCustomizer,一个非常明显的Customizer模式的类的玩法,代码如下:

private static class RequestFactoryCustomizer implements Consumer<ClientHttpRequestFactory> {
        private final Duration connectTimeout;
        private final Duration readTimeout;
        private final Boolean bufferRequestBody;

        RequestFactoryCustomizer() {
            this((Duration)null, (Duration)null, (Boolean)null);
        }

        private RequestFactoryCustomizer(Duration connectTimeout, Duration readTimeout, Boolean bufferRequestBody) {
            this.connectTimeout = connectTimeout;
            this.readTimeout = readTimeout;
            this.bufferRequestBody = bufferRequestBody;
        }

        RequestFactoryCustomizer connectTimeout(Duration connectTimeout) {
            return new RequestFactoryCustomizer(connectTimeout, this.readTimeout, this.bufferRequestBody);
        }

        RequestFactoryCustomizer readTimeout(Duration readTimeout) {
            return new RequestFactoryCustomizer(this.connectTimeout, readTimeout, this.bufferRequestBody);
        }

        RequestFactoryCustomizer bufferRequestBody(boolean bufferRequestBody) {
            return new RequestFactoryCustomizer(this.connectTimeout, this.readTimeout, bufferRequestBody);
        }

        public void accept(ClientHttpRequestFactory requestFactory) {
            ClientHttpRequestFactory unwrappedRequestFactory = this.unwrapRequestFactoryIfNecessary(requestFactory);
            if (this.connectTimeout != null) {
                this.setConnectTimeout(unwrappedRequestFactory);
            }

            if (this.readTimeout != null) {
                this.setReadTimeout(unwrappedRequestFactory);
            }

            if (this.bufferRequestBody != null) {
                this.setBufferRequestBody(unwrappedRequestFactory);
            }

        }

    }

可以看到,对外提供了一个accept的方法,用于进行了自定义设置扩展。

本文到这里就结束了,如果对你有帮助,收藏、关注、分享、点赞哦