What is the Java Supplier interface?

java.util.function.Supplier<T> is one of Java's core functional interfaces. A Supplier accepts no arguments and returns a value of type T.

Conceptually, its single abstract method looks like this:

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

A Supplier is useful whenever code needs a value but should not care exactly how that value is created. The supplied value might come from a calculation, object factory, cache, random-number generator or another source.

Supplier vs. Consumer vs. Function

The three interfaces describe different data flows:

  • Supplier<T> takes no argument and returns a value.
  • Consumer<T> accepts a value and returns no result.
  • Function<T,R> accepts a value and returns another value.

A Supplier does not guarantee that every call returns a new or distinct object. Repeated calls are allowed to return the same value.

Java Supplier interface example
A Java Supplier can be implemented by a class, lambda expression or method reference.

Implement Supplier with a Java class

The following class supplies a random digit from 0 through 9. A single Random instance is reused rather than creating a new random-number generator every time get() is called.

package com.mcnz.supplier.example;

import java.util.random.RandomGenerator;
import java.util.function.Supplier;

public class RandomDigitSupplier
        implements Supplier<Integer> {

    private final RandomGenerator random = RandomGenerator.getDefault();

    @Override
    public Integer get() {
        return random.nextInt(10);
    }
}

To use the Supplier, create it once and call get() whenever a value is needed.

public class SupplierExampleRunner {

    public static void main(String[] args) {
        Supplier<Integer> digits =
            new RandomDigitSupplier();

        for (int i = 0; i < 10; i++) {
            IO.print(digits.get() + " ");
        }
    }
}

A test run of the Supplier interface example generated the following results:

5 4 0 0 9 0 4 2 4 5

Supplier lambda expression example

Because Supplier<T> is a functional interface, a separate implementation class is often unnecessary. The same behavior can be expressed with a lambda:

import java.util.random.RandomGenerator;
import java.util.function.Supplier;

public class SupplierLambdaExample {

    public static void main(String[] args) {
        var random = RandomGenerator.getDefault();

        Supplier<Integer> digitSupplier =
            () -> random.nextInt(10);

        for (int i = 0; i < 10; i++) {
            IO.print(digitSupplier.get() + " ");
        }
    }
}

The lambda has no parameters, which is why it begins with empty parentheses. Its expression returns the value produced by Supplier.get().

Supplier with a method reference

If an existing no-argument method already returns the required type, a method reference can be even simpler.

import java.util.UUID;
import java.util.function.Supplier;

Supplier<UUID> uuidSupplier =
    UUID::randomUUID;

UUID id = uuidSupplier.get();

Here, UUID.randomUUID() takes no arguments and returns a UUID, so its signature matches Supplier<UUID>.

Primitive Supplier interfaces

Java also provides primitive specializations that avoid boxing values into wrapper objects:

  • IntSupplier returns an int through getAsInt().
  • LongSupplier returns a long through getAsLong().
  • DoubleSupplier returns a double through getAsDouble().
  • BooleanSupplier returns a boolean through getAsBoolean().
import java.util.random.RandomGenerator;
import java.util.function.IntSupplier;

var random = RandomGenerator.getDefault();

IntSupplier digitSupplier =
    () -> random.nextInt(10);

int digit = digitSupplier.getAsInt();

If the supplied result is naturally primitive, these specialized interfaces can be preferable to Supplier<Integer>, Supplier<Long> or Supplier<Double>.

Use Supplier with Stream.generate()

One of the clearest uses of Supplier in the standard Java API is Stream.generate(). The stream repeatedly calls a Supplier to obtain elements.

import java.util.UUID;
import java.util.stream.Stream;

Stream.generate(UUID::randomUUID)
      .limit(5)
      .forEach(IO::println);

Without limit(), a stream created by Stream.generate() is unbounded, so a terminating operation normally needs some way to restrict how many elements are consumed.

When should you use Supplier?

Use Supplier<T> when a caller needs to obtain a value without supplying an input argument. Common examples include lazy value creation, factories, default-value generation, deferred calculations and APIs such as Stream.generate().

The easiest way to remember Java's Supplier interface is by its shape: no input, one output. Once that model is clear, Supplier lambdas and method references become straightforward.