The Suplier interface

The Supplier interface denotes a lambda expression that generates an object when invoked. It is commonly used to implement lazy initialisation. This is where we want to declare in advance how to create a particular object if and when it is required, rather than having to create it up front. The Supplier implementation is the piece of code that determines how to initialise the object in question.

Lambda expressions that can be used as Suppliers

A lambda expression is a Supplier if:

Note the first of the conditions above. In some cases, lazy initialisation may also be performed by a lambda expression that is actually strictly speaking a Function because it needs an input value in order to determine what to initialise.

Syntax of a Supplier

Because a Supplier does not have any arguments, its syntax will have a characteristic () to denote the empty argument list:

Supplier<List> listMaker = () -> new ArrayList();

Direct references to no-arg constructors can also be used as Suppliers. To do this, put the class name followed by ::new as follows:

Supplier<List> listMaker = ArrayList::new;

Common uses of Supplier

As mentioned, a common use of a Supplier is for lazy initialisation. A concrete example of this is the ThreadLocal.withInitial() method. In case you have not come across them, a thread local variable has an independent value for each thread that references it. Whenever a given thread requests the value, it retrieves the value that was last set by that specific thread; other threads can also retrieve their thread-specific values at any time. But this leaves the question: what value is returned the first time that a given thread attempts to retrieve the value?

We don't generally want the overhead of having to set an initial value in advance for every single thread running on the system, without knowing that a particular thread is actually going to want to refer it. (Nor do we want the overhead of having to search for all of the thread-local variables to initialise them every time a new thread is started.) And this is where ThreadLocal.withInitial() comes to the rescue. We pass in a lambda expression that determines how to create the initial value as and when it is required by a particular thread:

ThreadLocal<Calendar> cal =
  ThreadLocal.withInitial(() -> Calendar.getInstance());

We could also write:

ThreadLocal<Calendar> cal =
  ThreadLocal.withInitial(Calendar::getInstance);

At this point, no calendars have actually been constructed. The very first time a given thread calls cal.get(), the Supplier will be invoked, and a new Calendar will be created for that thread. The next time the same thread calls cal.get(), that same calendar object will then be returned to it. But no unnecessary objects are created if a given thread never actually requires the use of a calendar.

Using Supplier with switchable configuration

Another common use of Supplier is with switchable configuration. In other words, we have particular lines of code that we would like to execute if and only if a particular condition is true. For example, we might have some lines of debug that we want to log out if debugging mode is enabled. We could write a central logging method such as the following:

public static void debugLog(String msg) {
  if (DEBUG) {
    System.out.println(msg);
  }
}

This is functionally sound, but has a minor disadvantage if the logging messages themselves are complex to generate. Consider this example:

debugLog(String.format("%03d Command: %s", cmdNo, lookupCommand(code)));

Here, our code will go to the effort of looking up the command and formatting the string even if debug logging is actually turned off!

The Supplier interface provides a solution to this. We can define our logging method as follows:

public static void debugLog(Supplier<String> msg) {
  if (DEBUG) {
    System.out.println(msg.get());
  }
}

We would log information like this:

debugLog(() -> String.format("%03d Command: %s", cmdNo, lookupCommand(code)));

Now, thanks to lazy initialisation of the log message, the lookup and formatting operation are only performed if they are actually required.


If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.