badCoder
badCoder

Reputation: 770

When we should use Supplier in Java 8?

What difference between this code?

Supplier<LocalDate> s1 = LocalDate::now;
LocalDate s2 = LocalDate.now();

System.out.println(s1.get()); //2016-10-25
System.out.println(s2); //2016-10-25

I start learning functional interfaces in Java 8 and don't understand the benefit of the Supplier. When and how, exactly, should use them. Does the Supplier improve performance or maybe the benefits on abstraction level?

Thanks for your answers! And it isn't duplicate question because I used search and didn't find what I need.

UPDATE 1: You mean this?

    Supplier<Long> s1 = System::currentTimeMillis;
    Long s2 = System.currentTimeMillis();

    System.out.println(s1.get()); //1477411877817
    System.out.println(s2); //1477411877817
    try {
        Thread.sleep(3000l);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(s1.get()); //1477411880817 - different
    System.out.println(s2); //1477411877817

Upvotes: 58

Views: 42074

Answers (9)

Ghulam Asghar Dahri
Ghulam Asghar Dahri

Reputation: 55

One of the primary usage of this interface to enable deferred execution. This means delaying the execution until it is needed. For example, Optional class has a method named orElseGet. This method is triggered if optional does not have data. This is demonstrated below:

@Test public void supplierWithOptional(){ Supplier doubleSupplier = () -> Math.random(); Optional optionalDouble = Optional.empty(); System.out.println(optionalDouble.orElseGet(doubleSupplier)); }

Credit: https://medium.com/swlh/understanding-java-8s-consumer-supplier-predicate-and-function-c1889b9423d

Upvotes: 1

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79015

Does the Supplier improve performance or maybe the benefits on abstraction level?

No, it's not meant to improve performance. The Supplier is used for a deferred execution i.e. you specify a functionality (code) that will run whenever used. The following example demonstrates the difference:

import java.time.LocalDateTime;
import java.util.function.Supplier;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        // Create a reference to the current date-time object when the following line is
        // executed
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);// Line-1

        // Create a reference to a functionality that will get the current date-time
        // whenever this functionality will be used
        Supplier<LocalDateTime> dateSupplier = LocalDateTime::now;

        // Sleep for 5 seconds
        Thread.sleep(5000);

        System.out.println(ldt);// Will display the same value as Line-1
        System.out.println(dateSupplier.get());// Will display the current date-time when this line will be executed

        // Sleep again for 5 seconds
        Thread.sleep(5000);

        System.out.println(ldt);// Will display the same value as Line-1
        System.out.println(dateSupplier.get());// Will display the current date-time when this line will be executed
    }
}

Output of a sample run:

2021-04-11T00:04:06.205105
2021-04-11T00:04:06.205105
2021-04-11T00:04:11.211031
2021-04-11T00:04:06.205105
2021-04-11T00:04:16.211659

Another useful case:

import java.util.List;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("Hello", "B2C", "World", "Stack Overflow", "is", "a", "gr8", "platform");

        // A simple Stream for demo; you can think of a complex Stream with more
        // intermediate operations
        Stream<String> stream = list.stream()
                                    .filter(s -> s.length() <= 5)
                                    .map(s -> s.substring(1));

        System.out.println(stream.anyMatch(s -> Character.isLetter(s.charAt(0))));
        System.out.println(stream.anyMatch(s -> Character.isDigit(s.charAt(0))));
    }
}

Output:

true
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
    at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
    at Main.main(Main.java:13)

The output is self-explanatory. An ugly workaround could be creating a new Stream each time as shown below:

import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("Hello", "B2C", "World", "Stack Overflow", "is", "a", "gr8", "platform");

        System.out.println(list.stream().filter(s -> s.length() <= 5).map(s -> s.substring(1))
                .anyMatch(s -> Character.isLetter(s.charAt(0))));
        
        System.out.println(list.stream().filter(s -> s.length() <= 5).map(s -> s.substring(1))
                .anyMatch(s -> Character.isDigit(s.charAt(0))));
    }
}

Now, see how cleanly you can do it using a Supplier:

import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        List<String> list = List.of("Hello", "B2C", "World", "Stack Overflow", "is", "a", "gr8", "platform");

        Supplier<Stream<String>> streamSupplier = () -> list.stream()
                                                            .filter(s -> s.length() <= 5)
                                                            .map(s -> s.substring(1));

        System.out.println(streamSupplier.get().anyMatch(s -> Character.isLetter(s.charAt(0))));

        System.out.println(streamSupplier.get().anyMatch(s -> Character.isDigit(s.charAt(0))));
    }
}

Output:

true
true

Upvotes: 30

newday
newday

Reputation: 3878

This example of how Supplier can be used to improve the performance but Supplier itself will not improve the performance.

/**
 * Checks that the specified object reference is not {@code null} and
 * throws a customized {@link NullPointerException} if it is.
 *
 * <p>Unlike the method {@link #requireNonNull(Object, String)},
 * this method allows creation of the message to be deferred until
 * after the null check is made. While this may confer a
 * performance advantage in the non-null case, when deciding to
 * call this method care should be taken that the costs of
 * creating the message supplier are less than the cost of just
 * creating the string message directly.
 *
 * @param obj     the object reference to check for nullity
 * @param messageSupplier supplier of the detail message to be
 * used in the event that a {@code NullPointerException} is thrown
 * @param <T> the type of the reference
 * @return {@code obj} if not {@code null}
 * @throws NullPointerException if {@code obj} is {@code null}
 * @since 1.8
 */
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
    if (obj == null)
        throw new NullPointerException(messageSupplier == null ?
                                       null : messageSupplier.get());
    return obj;
}

In this method, the Supplier use in a way that only the object is really null then gets the String. Therefore, it can improve the performance of the operation but it is not Supplier but how it has been used.

Upvotes: 1

Vivek MVK
Vivek MVK

Reputation: 1179

I will add my view as I am not satisfied by the answers: Supplier is useful when you want to delay the execution.

Without supplier

config.getLocalValue(getFromInternet() /*value if absent*/);

Before getLocalValue gets called getFromInternet will be called, but the value of getFromInternet() will be used only if local value is absent.

Now, if config.getLocalValue can accept supplier, we can delay this execution, plus we won't execute if local value is present.

config.getLocalValue(() -> getFromInternet())

Difference Supplier makes it possible: execute only when and if needed

Upvotes: 6

Htnamus
Htnamus

Reputation: 76

The explanation in Item 5 of Effective Java by Joshua Bloch cleared this up for me:

The Supplier interface, introduced in Java 8, is perfect for representing factories. Methods that take a Supplier on input should typically constrain the factory’s type parameter using a bounded wildcard type (Item 31) to allow the client to pass in a factory that creates any subtype of a specified type. For example, here is a method that makes a mosaic using a client-provided factory to produce each tile:

Mosaic create(Supplier<? extends Tile> tileFactory) { ... }

You might want to send in a resource factory to an object which will make objects using the resource factory. Now, let's say you want to send in different resource factories with each resource factory generating objects in different positions of the inheritance tree and yet the object receiving the resource factory must be able to receive them in any case.

Then you can implement a supplier which can accept methods that return objects extending/implementing a certain class/interface using the bounded wildcard and use the supplier as a resource factory.

Reading Item 5 of the book might help in completely understanding the usage.

Upvotes: 4

Ed Bighands
Ed Bighands

Reputation: 169

I am sure your query has been answered by the answers provided already. This is my understanding of Supplier.

It give me the default Java defined interfaces that acts as a wrapper for any lambda method ref target return time. Simply put, anywhere you have written a method that has no args and returns an Object, qualifies to be wrapped by the Supplier Interface. Without that, in pre generics, you would capture the return as object class and then cast and in generics you would define your own T(ype) to hold the return value. It just provides a uniform return type generic holder on which you can invoke get() to extract the returned object from the no arg method mentioned above.

Upvotes: 2

joshden
joshden

Reputation: 798

I'll go through a scenario where we should use Supplier<LocalDate> instead of LocalDate.

Code that directly makes calls to static methods like LocalDate.now() is very difficult to unit test. Consider a scenario where we want to unit test a method getAge() that calculates a person's age:

class Person {
    final String name;
    private final LocalDate dateOfBirth;

    Person(String name, LocalDate dateOfBirth) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
    }

    long getAge() {
        return ChronoUnit.YEARS.between(dateOfBirth, LocalDate.now());
    }
}

This works fine in production. But a unit test would either have to set the system's date to a known value or be updated every year to expect the returned age to be incremented by one, both pretty aweful solutions.

A better solution would be for the unit test to inject in a known date while still allowing the production code to use LocalDate.now(). Maybe something like this:

class Person {
    final String name;
    private final LocalDate dateOfBirth;
    private final LocalDate currentDate;

    // Used by regular production code
    Person(String name, LocalDate dateOfBirth) {
        this(name, dateOfBirth, LocalDate.now());
    }

    // Visible for test
    Person(String name, LocalDate dateOfBirth, LocalDate currentDate) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
        this.currentDate = currentDate;
    }

    long getAge() {
        return ChronoUnit.YEARS.between(dateOfBirth, currentDate);
    }

}

Consider a scenario where the person's birthday has passed since the object was created. With this implementation, getAge() will be based on when the Person object was created rather than the current date. We can solve this by using Supplier<LocalDate>:

class Person {
    final String name;
    private final LocalDate dateOfBirth;
    private final Supplier<LocalDate> currentDate;

    // Used by regular production code
    Person(String name, LocalDate dateOfBirth) {
        this(name, dateOfBirth, ()-> LocalDate.now());
    }

    // Visible for test
    Person(String name, LocalDate dateOfBirth, Supplier<LocalDate> currentDate) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
        this.currentDate = currentDate;
    }

    long getAge() {
        return ChronoUnit.YEARS.between(dateOfBirth, currentDate.get());
    }

    public static void main(String... args) throws InterruptedException {
        // current date 2016-02-11
        Person person = new Person("John Doe", LocalDate.parse("2010-02-12"));
        printAge(person);
        TimeUnit.DAYS.sleep(1);
        printAge(person);
    }

    private static void printAge(Person person) {
        System.out.println(person.name + " is " + person.getAge());
    }
}

The output would correctly be:

John Doe is 5
John Doe is 6

Our unit test can inject the "now" date like this:

@Test
void testGetAge() {
    Supplier<LocalDate> injectedNow = ()-> LocalDate.parse("2016-12-01");
    Person person = new Person("John Doe", LocalDate.parse("2004-12-01"), injectedNow);
    assertEquals(12, person.getAge());
}

Upvotes: 46

Holger
Holger

Reputation: 298123

You are confusing functional interfaces and method references. Supplier is just an interface, similar to Callable, which you should know since Java 5, the only difference being that Callable.call is allowed to throw checked Exceptions, unlike Supplier.get. So these interfaces will have similar use cases.

Now, these interface also happen to be functional interfaces, which implies that they can be implemented as a method reference, pointing to an existing method that will be invoked when the interface method is invoked.

So before Java 8, you had to write

Future<Double> f=executorService.submit(new Callable<Double>() {
    public Double call() throws Exception {
        return calculatePI();
    }
});
/* do some other work */
Double result=f.get();

and now, you can write

Future<Double> f=executorService.submit(() -> calculatePI());
/* do some other work */
Double result=f.get();

or

Future<Double> f=executorService.submit(MyClass::calculatePI);
/* do some other work */
Double result=f.get();

The question when to use Callable hasn’t changed at all.

Similarly, the question when to use Supplier is not dependent on how you implement it, but which API you use, i.e.

CompletableFuture<Double> f=CompletableFuture.supplyAsync(MyClass::calculatePI);
/* do some other work */
Double result=f.join();// unlike Future.get, no checked exception to handle...

Upvotes: 11

B&#225;lint
B&#225;lint

Reputation: 4039

It definitely doesn't improve the performance. Your question is similar to this one: Why are we using variables? We could simply just recalculate everything every time we need it. Right?

If you need to use a method a lot of times, but it has a wordy syntax.

Let's assume you have a class named MyAmazingClass, and you have a method in it with the name MyEvenBetterMethod (which is static), and you need to call it 15 times at 15 different positions in your code. Of course, you can do something like...

int myVar = MyAmazingClass.MyEvenBetterMethod();
// ...
int myOtherVar = MyAmazingClass.MyEvenBetterMethod();
// And so on...

...but you can also do

Supplier<MyAmazingClass> shorter = MyAmazingClass::MyEvenBetterMethod;

int myVar = shorter.get();
// ...
int myOtherVar = shorter.get();
// And so on...

Upvotes: 24

Related Questions