Java Supplier Functional Interface: get(), Lazy Evaluation & Practical Examples

0

Supplier

A Supplier is a functional interface in Java used when you need to obtain a value without providing an input value. It represents the idea of "give me a value when I ask for one."

This makes Supplier useful for generating values, creating objects, reading configuration values, producing default values, and delaying an operation until the value is actually needed.

What Is Supplier?

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

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

The interface has one abstract method, get(). It accepts no arguments and returns a value of type T.

Remember: Supplier takes no input and produces one output. Its primary method is get().

Basic Supplier Example

import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) {
        Supplier<String> message =
            () -> "Welcome to Java";

        System.out.println(message.get());
    }
}

The lambda has no parameters because the Supplier does not require input. When get() is called, the lambda produces a string.

Understanding Supplier<T>

The generic type T specifies the type of value that the Supplier produces.

Supplier Input Output Method
Supplier<String> None String get()
Supplier<Integer> None Integer get()
Supplier<Double> None Double get()
Supplier<Student> None Student get()

Supplier with Numbers

A Supplier can generate a number whenever its get() method is called.

import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) {
        Supplier<Integer> number =
            () -> 100;

        System.out.println(number.get());
    }
}

Every call returns the value produced by the lambda.

Supplier Can Produce Different Values

A Supplier does not have to return a fixed value. It can calculate or generate a value each time it is called.

import java.util.function.Supplier;
import java.util.Random;

public class Main {
    public static void main(String[] args) {
        Supplier<Integer> randomNumber =
            () -> new Random().nextInt(100);

        System.out.println(randomNumber.get());
        System.out.println(randomNumber.get());
    }
}

The Supplier creates a value when requested. Because the operation is executed during each get() call, the returned values can differ.

Supplier with Multiple Statements

A Supplier can contain multiple statements by using a block lambda.

Supplier<Integer> calculate =
    () -> {
        int first = 20;
        int second = 30;
        return first + second;
    };

System.out.println(calculate.get());

Because the Supplier returns a value, a block-bodied lambda must use an explicit return statement.

Supplier with Custom Objects

A Supplier can also create and return application objects.

import java.util.function.Supplier;

class Student {
    String name;

    Student(String name) {
        this.name = name;
    }
}

public class Main {
    public static void main(String[] args) {
        Supplier<Student> studentSupplier =
            () -> new Student("Ravi");

        Student student = studentSupplier.get();

        System.out.println(student.name);
    }
}

The Supplier stores the object-creation behavior. The actual Student object is created when get() is called.

Supplier for Default Values

Supplier is useful when a value should be generated only when it is actually required.

import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) {
        Supplier<String> defaultMessage =
            () -> "Default message";

        System.out.println(defaultMessage.get());
    }
}

This pattern becomes particularly useful when generating the fallback value is expensive and should not happen unnecessarily.

Supplier and Lazy Evaluation

One important idea associated with Supplier is lazy evaluation. Instead of calculating a value immediately, you can store the operation and execute it later.

Supplier<String> message =
    () -> {
        System.out.println("Creating message...");
        return "Hello";
    };

System.out.println("Before get()");
System.out.println(message.get());

The code inside the Supplier does not execute when the Supplier is created. It executes when get() is called.

Think of a Supplier as a vending machine: you do not receive anything when you simply know the machine exists. You receive the value when you request it.

Supplier as a Method Parameter

A method can accept a Supplier when it needs a value but wants the caller to decide how that value should be produced.

import java.util.function.Supplier;

public class Main {

    static void display(Supplier<String> supplier) {
        System.out.println(supplier.get());
    }

    public static void main(String[] args) {
        display(() -> "Java Programming");
        display(() -> "Spring Boot");
    }
}

The display() method does not know where the text comes from. It simply asks the Supplier for a value.

Supplier with a Method

A Supplier can also reference an existing method.

import java.util.function.Supplier;

public class Main {

    static String getMessage() {
        return "Hello from Java";
    }

    public static void main(String[] args) {
        Supplier<String> supplier =
            Main::getMessage;

        System.out.println(supplier.get());
    }
}

The method reference Main::getMessage provides the implementation expected by the Supplier.

Supplier vs Consumer

Supplier and Consumer represent opposite directions of data flow. A Supplier produces a value, while a Consumer receives a value and performs an action.

Feature Supplier Consumer
Input None One value
Output One value None
Main method get() accept()
Purpose Produce a value Perform an action

Supplier vs Function

A Function transforms an existing input, while a Supplier creates or provides a value without requiring input.

Feature Supplier Function
Input None One value
Output One value One value
Main method get() apply()
Purpose Provide or generate a value Transform a value

Supplier vs Predicate

Predicate tests a supplied input and returns a boolean. Supplier does not accept input and instead produces a value.

Predicate<Integer> positive =
    number -> number > 0;

Supplier<Integer> number =
    () -> 100;

The Predicate asks a question about a value. The Supplier provides a value.

Practical Example: Generating User IDs

import java.util.function.Supplier;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) {
        AtomicInteger counter =
            new AtomicInteger(1000);

        Supplier<Integer> userId =
            () -> counter.incrementAndGet();

        System.out.println(userId.get());
        System.out.println(userId.get());
        System.out.println(userId.get());
    }
}

The Supplier encapsulates the ID-generation logic. Every call to get() produces the next identifier.

Practical Example: Generating Configuration Values

import java.util.function.Supplier;

public class Main {

    static void startApplication(
            Supplier<String> environment) {

        System.out.println(
            "Environment: " + environment.get()
        );
    }

    public static void main(String[] args) {
        startApplication(() -> "Production");
    }
}

The method receives a Supplier instead of a fixed string. This keeps value-generation logic separate from the code that consumes the value.

Common Beginner Mistakes

  • Trying to pass a parameter to get().
  • Forgetting that Supplier does not accept an input value.
  • Confusing Supplier with Function, which requires an input.
  • Assuming a Supplier always returns the same value.
  • Calling get() repeatedly when the generated operation is expensive and the result should instead be cached.

Best Practices

  • Use Supplier when a value should be produced without an input.
  • Use Supplier to defer value creation until it is actually needed.
  • Keep the supplied operation focused and predictable.
  • Use method references when they make the Supplier easier to read.
  • Be aware that each call to get() can execute the supplied logic again.

Interview Insight

Interview shortcut: Supplier<T> takes no arguments and returns a value of type T through its get() method. It is commonly used for value generation, lazy evaluation, and deferred object creation.

Quick Revision

Concept Key Point
Supplier<T> Produces a value without receiving input
get() Requests the supplied value
Input None
Output One value of type T
Lazy Evaluation Logic executes when get() is called
Typical Use Value generation, defaults, object creation, deferred operations

Final Takeaway

A Supplier is the functional interface to remember whenever you need to produce a value without receiving an input. Its simple get() method makes it useful for generating values, creating objects, supplying defaults, and delaying work until it is actually required. Once you recognize the pattern "nothing goes in, something comes out," choosing Supplier becomes straightforward.

Post a Comment

0Comments
Post a Comment (0)