Reputation: 770
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
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
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
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
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
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
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
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
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 Exception
s, 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
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