Java Consumer Functional Interface: accept(), Lambda Expressions & Examples

0

A Consumer is a functional interface in Java used when you want to accept a value and perform an action without returning a result. In simple terms, a Consumer answers the question: "What should I do with this value?"

Printing a name, saving an object, logging information, sending a notification, or updating a record are all examples of operations that can be represented by a Consumer.

What Is Consumer?

The Consumer<T> interface belongs to the java.util.function package.

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

The interface contains one abstract method named accept(). It receives one value and returns nothing.

Remember: Predicate asks a question and returns boolean. Consumer receives a value and performs an action, but returns void.

Basic Consumer Example

import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Consumer<String> printer =
            message -> System.out.println(message);

        printer.accept("Hello, Java!");
    }
}

The lambda receives a string and prints it. The accept() method supplies the actual value to the Consumer.

Understanding Consumer<T>

The generic type T represents the type of value that the Consumer accepts.

Consumer Input Return Value Method
Consumer<String> String void accept()
Consumer<Integer> Integer void accept()
Consumer<Student> Student void accept()

Unlike Function, a Consumer does not transform its input into another value. Unlike Predicate, it does not produce a boolean result.

Consumer with Integer

import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Consumer<Integer> display =
            number -> System.out.println("Number: " + number);

        display.accept(100);
    }
}

The Consumer receives an integer and performs an output operation. There is no returned value.

Consumer with Custom Objects

Consumers become especially useful when processing application objects.

import java.util.function.Consumer;

class Student {
    String name;
    int marks;

    Student(String name, int marks) {
        this.name = name;
        this.marks = marks;
    }
}

public class Main {
    public static void main(String[] args) {
        Consumer<Student> displayStudent =
            student -> System.out.println(
                student.name + " - " + student.marks
            );

        Student student = new Student("Ravi", 85);

        displayStudent.accept(student);
    }
}

The Consumer knows how to display a Student object. The calling code only needs to provide the object.

Consumer with Collections

One of the most common places you will encounter Consumer is the forEach() method.

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList(
            "Ravi",
            "Anita",
            "Priya",
            "Amit"
        );

        names.forEach(
            name -> System.out.println(name)
        );
    }
}

The forEach() operation accepts a Consumer. For every element in the list, the lambda receives that element and performs the specified action.

Storing a Consumer in a Variable

A Consumer can be stored in a variable and reused.

Consumer<String> printer =
    text -> System.out.println("Value: " + text);

printer.accept("Java");
printer.accept("Spring Boot");
printer.accept("SQL");

This is useful when the same operation needs to be performed in several places.

Passing Consumer to a Method

A method can accept a Consumer as an argument. This allows the caller to decide what action should happen.

import java.util.function.Consumer;

public class Main {

    static void process(String value, Consumer<String> action) {
        action.accept(value);
    }

    public static void main(String[] args) {
        process("Java",
            text -> System.out.println(
                "Processing: " + text
            )
        );
    }
}

The process() method does not contain a fixed action. The behavior is supplied by the caller through the Consumer.

Consumer with Multiple Statements

A Consumer can contain multiple statements when a block lambda is used.

Consumer<String> process =
    text -> {
        System.out.println("Received: " + text);
        System.out.println("Length: " + text.length());
    };

process.accept("Lambda");

Because Consumer's method returns void, there is no return statement in the lambda.

Consumer and Side Effects

Consumers are commonly associated with side effects. A side effect means the operation changes something outside the calculation itself, such as printing output, updating a database, modifying an object, or writing a log.

Consumer<String> logger =
    message -> System.out.println(
        "LOG: " + message
    );

The purpose here is not to calculate a new value. The purpose is to perform an action.

When deciding whether Consumer is appropriate, ask: "Do I need to perform an action but do not need a result back?" If the answer is yes, Consumer is likely a good match.

Consumer Chaining with andThen()

One of the most useful features of Consumer is the andThen() method. It allows two Consumers to be executed sequentially.

import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Consumer<String> first =
            text -> System.out.println("First: " + text);

        Consumer<String> second =
            text -> System.out.println("Second: " + text);

        Consumer<String> combined =
            first.andThen(second);

        combined.accept("Java");
    }
}

When combined.accept("Java") executes, the first Consumer runs before the second Consumer.

Chaining More Than Two Consumers

Consumers can be chained multiple times when a sequence of actions needs to be performed.

Consumer<String> first =
    text -> System.out.println("Step 1: " + text);

Consumer<String> second =
    text -> System.out.println("Step 2: " + text);

Consumer<String> third =
    text -> System.out.println("Step 3: " + text);

Consumer<String> workflow =
    first.andThen(second).andThen(third);

workflow.accept("Processing");

This technique can be useful when several small actions form a predictable sequence.

Consumer vs Predicate

Both interfaces accept one value, but their purposes are different.

Feature Predicate Consumer
Input One value One value
Return type boolean void
Main method test() accept()
Purpose Check a condition Perform an action
Typical use Filtering Processing or displaying

Consumer vs Function

The easiest way to distinguish Consumer from Function is to ask whether you need a result.

Feature Consumer Function
Input One value One value
Output None One value
Method accept() apply()
Purpose Perform an action Transform a value

Practical Example: Processing Orders

import java.util.*;
import java.util.function.Consumer;

class Order {
    int id;
    double amount;

    Order(int id, double amount) {
        this.id = id;
        this.amount = amount;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Order> orders = Arrays.asList(
            new Order(101, 2500),
            new Order(102, 1800),
            new Order(103, 4200)
        );

        Consumer<Order> processOrder =
            order -> System.out.println(
                "Processing order: " + order.id
            );

        orders.forEach(processOrder);
    }
}

The Consumer represents the action to perform for every order. If the application later needs a different processing behavior, another Consumer can be supplied without changing the collection traversal logic.

Consumer in Real Applications

  • Logging application events.
  • Displaying objects or values.
  • Sending notifications.
  • Updating existing objects.
  • Processing records from a collection.
  • Executing a sequence of operations using andThen().

Common Beginner Mistakes

  • Trying to return a value from a Consumer.
  • Calling test() instead of accept().
  • Using Consumer when the operation actually needs to produce a transformed result.
  • Putting too much business logic inside a single Consumer.
  • Assuming that every lambda accepting one parameter is automatically a Consumer.

Best Practices

  • Use Consumer when an operation needs an input but does not need to return a result.
  • Keep Consumer logic small and focused.
  • Use meaningful variable names when storing Consumers.
  • Use andThen() when sequential actions are easier to understand as separate operations.
  • Avoid unnecessary side effects when a pure transformation would be better represented by Function.

Interview Insight

Interview shortcut: Consumer<T> takes one argument of type T, returns nothing, and uses the accept() method. Its most common role is performing an action on a supplied value.

Quick Revision

Concept Key Point
Consumer<T> Accepts one value and performs an action
accept() Executes the Consumer with the supplied value
Return type void
forEach() Commonly accepts a Consumer
andThen() Chains Consumers in execution order
Typical use Printing, logging, updating, processing, notifications

Final Takeaway

A Consumer represents an action that accepts a value without producing a result. Its accept() method makes it ideal for processing objects, printing information, logging events, and working with collection operations such as forEach(). Once you understand the simple rule "input goes in, action happens, nothing comes back," Consumer becomes one of the easiest and most practical functional interfaces to use in Java.

Post a Comment

0Comments
Post a Comment (0)