ZetCode

Java Consumer 接口

最后修改时间:2025 年 4 月 16 日

java.util.function.Consumer 接口表示一个接受单个输入参数且不返回任何结果的操作。它是一个函数式接口,具有一个抽象方法 accept。Consumer 用于产生副作用的操作,例如打印或修改对象。

Consumer 是 Java 8 中添加的 Java 函数式编程工具的一部分。与大多数函数式接口不同,Consumer 操作旨在产生副作用。该接口提供了用于消费者链的默认方法。

Consumer 接口概述

Consumer 接口包含一个抽象方法和一个默认方法。关键方法 accept 对输入执行操作。andThen 方法启用消费者链。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    
    default Consumer<T> andThen(Consumer<? super T> after);
}

上面的代码显示了 Consumer 接口的结构。它使用泛型,其中 T 是输入类型。该接口使用 @FunctionalInterface 注解进行注释,以表明其单抽象方法的性质。

基本 Consumer 用法

使用 Consumer 的最简单方法是使用 lambda 表达式。我们在 accept 方法中定义如何处理输入。示例将字符串打印到控制台。

Main.java
package com.zetcode;

import java.util.function.Consumer;

public class Main {

    public static void main(String[] args) {

        // Define a consumer that prints strings
        Consumer<String> printer = s -> System.out.println(s);
        
        // Use the consumer
        printer.accept("Hello, Consumer!");
        
        // Consumer using method reference
        Consumer<String> methodRefPrinter = System.out::println;
        methodRefPrinter.accept("Method reference consumer");
    }
}

此示例演示了使用 lambda 和方法引用的基本 Consumer 用法。打印机 consumer 接受一个 String 并将其打印出来。方法引用为与 Consumer 签名匹配的现有方法提供了更简洁的语法。

Consumer 与集合

Consumer 经常通过 forEach 方法与集合一起使用。这使得可以在没有显式循环的情况下进行干净的迭代模式。示例处理一个数字列表。

Main.java
package com.zetcode;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class Main {

    public static void main(String[] args) {

        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // Consumer to print numbers with prefix
        Consumer<Integer> numberPrinter = n -> 
            System.out.println("Number: " + n);
        
        // Apply consumer to each element
        numbers.forEach(numberPrinter);
        
        // Inline consumer
        numbers.forEach(n -> System.out.println(n * 2));
    }
}

此示例显示了 Consumer 与集合的用法。我们定义了一个数字打印机 consumer 并将其传递给 forEach。第二个 forEach 演示了一个内联 consumer。这两种方法都处理每个集合元素。

使用 andThen 进行 Consumer 链

andThen 方法允许链接消费者,其中每个消费者按顺序处理相同的输入。这使得可以从简单的消费者创建复杂的处理管道。

Main.java
package com.zetcode;

import java.util.function.Consumer;

public class Main {

    public static void main(String[] args) {

        // First consumer logs the message
        Consumer<String> logger = s -> 
            System.out.println("LOG: " + s);
        
        // Second consumer sends email notification
        Consumer<String> notifier = s -> 
            System.out.println("Sending notification: " + s);
        
        // Chain the consumers
        Consumer<String> processor = logger.andThen(notifier);
        
        // Process a message
        processor.accept("System started successfully");
    }
}

此示例显示了使用 andThen 的消费者链。输入消息首先被记录,然后触发一个通知。两个消费者都接收相同的输入。保证执行顺序。

使用 Consumer 修改对象

Consumers 经常用于修改对象状态。由于它们接受参数但不返回任何内容,因此它们非常适合于变异操作。示例更新产品价格。

Main.java
package com.zetcode;

import java.util.function.Consumer;

class Product {
    String name;
    double price;
    
    Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    @Override
    public String toString() {
        return name + ": $" + price;
    }
}

public class Main {

    public static void main(String[] args) {

        Product laptop = new Product("Laptop", 999.99);
        
        // Consumer to apply discount
        Consumer<Product> discount = p -> 
            p.price = p.price * 0.9; // 10% discount
        
        System.out.println("Before: " + laptop);
        discount.accept(laptop);
        System.out.println("After: " + laptop);
    }
}

此示例演示了使用 Consumer 进行对象修改。折扣 consumer 接受一个 Product 并将其价格降低 10%。Consumers 非常适合此类更改状态的操作,同时保持代码清洁。

BiConsumer 接口

虽然 Consumer 接受一个参数,但 BiConsumer 处理两个输入。当操作需要两个参数时,它很有用。示例处理键值对。

Main.java
package com.zetcode;

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

public class Main {

    public static void main(String[] args) {

        Map<String, Integer> ages = new HashMap<>();
        ages.put("John", 25);
        ages.put("Jane", 30);
        ages.put("Bob", 35);
        
        // BiConsumer to print key-value pairs
        BiConsumer<String, Integer> agePrinter = 
            (name, age) -> System.out.println(name + " is " + age + " years old");
        
        // Process each map entry
        ages.forEach(agePrinter);
        
        // Inline BiConsumer
        ages.forEach((k, v) -> System.out.println(k + ": " + v));
    }
}

此示例显示了 BiConsumer 的用法。agePrinter 接受 map 键和值。Map 的 forEach 需要一个 BiConsumer。我们演示了命名的和内联的 BiConsumer 变体。

专门的 Consumers

Java 为原始类型提供了专门的 Consumer 接口,以避免装箱开销。这些包括 IntConsumerDoubleConsumerLongConsumer。示例使用原始消费者。

Main.java
package com.zetcode;

import java.util.function.IntConsumer;
import java.util.function.DoubleConsumer;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {

        int[] integers = {1, 2, 3, 4, 5};
        double[] doubles = {1.1, 2.2, 3.3, 4.4, 5.5};
        
        // IntConsumer example
        IntConsumer squarePrinter = i -> 
            System.out.println(i + " squared is " + (i * i));
        
        Arrays.stream(integers).forEach(squarePrinter);
        
        // DoubleConsumer example
        DoubleConsumer rounder = d -> 
            System.out.println(d + " rounded is " + Math.round(d));
        
        Arrays.stream(doubles).forEach(rounder);
    }
}

此示例演示了原始类型专用的消费者。IntConsumer 处理 int 值,无需装箱。DoubleConsumer 处理 double 值。这些专业化提高了对原始类型密集型操作的性能。

来源

Java Consumer 接口文档

在本文中,我们介绍了 Java Consumer 接口的基本方法和特性。理解这些概念对于现代 Java 应用程序中的函数式编程和集合处理至关重要。

作者

我的名字是 Jan Bodnar,是一位经验丰富的程序员,在这一领域拥有多年经验。我从 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和 8 本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程