You can also do:
expectedPackageRepository.findById(1).ifPresent(
ep -> {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
);
Ideally, you would also extract the part between brackets ({}) to a separate method. Then, you could write like this:
expectedPackageRepository.findById(1).ifPresent(this::doSomethingWithEp);
Where:
void doSomethingWithEp(ExpectedPackage ep) {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
You can read the documentation of ifPresent here: https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#ifPresent-java.util.function.Consumer-
As it states, it will perform the specified action if the value is present and do nothing otherwise.
Answer from Poger on Stack OverflowYou can also do:
expectedPackageRepository.findById(1).ifPresent(
ep -> {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
);
Ideally, you would also extract the part between brackets ({}) to a separate method. Then, you could write like this:
expectedPackageRepository.findById(1).ifPresent(this::doSomethingWithEp);
Where:
void doSomethingWithEp(ExpectedPackage ep) {
ep.setDateModified(new Date());
expectedPackageRepository.saveAndFlush(ep);
}
You can read the documentation of ifPresent here: https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#ifPresent-java.util.function.Consumer-
As it states, it will perform the specified action if the value is present and do nothing otherwise.
Yes, there are other approaches.
If you absolutely expect there always to be a value, then use Optional::orElseThrow to throw an Exception if a null appears.
If you expect a null to possibly arrive, and have an alternative instance available as a fall-back option, use Optional::orElse.
If the fall-back instance is not on hand, but you have a function to call to provide a fall-back instance, use Optional::orElseGet.
If you donβt care about receiving a null, and want to do nothing when a null arrives, use Optional::ifPresent. Pass the block of code to be run if a value arrives.
If you only care if a value arrives that meets some requirement, use Optional::filter. Pass a Predicate defining your requirement. For example, we care only if an Optional< String > contains text and that text has the word purple in it: myOptional.filter( s -> s.contains( "purple" ) ).ifPresent( this::print ) ;. If null received, our desired operation (a call to print in this example) never happens. If a value was received but failed to meet our predicate, our desired operation never happens.
Doing if( myOptional.isPresent() ) { SomeClass x = myOptional.get() ; β¦ } is valid, and safe. But this is not the original intent of Optional as it is basically the same as doing an old-fashioned null-check if ( null == x ) { β¦ }. The other methods on Optional provide a more clear and elegant way to express your intentions towards a possible null arriving.
Videos
Short Answer:
- orElse() will always call the given function whether you want it or not, regardless of
Optional.isPresent()value - orElseGet() will only call the given function when the
Optional.isPresent() == false
In real code, you might want to consider the second approach when the required resource is expensive to get.
Copy// Always get heavy resource
getResource(resourceId).orElse(getHeavyResource());
// Get heavy resource when required.
getResource(resourceId).orElseGet(() -> getHeavyResource())
For more details, consider the following example with this function:
Copypublic Optional<String> findMyPhone(int phoneId)
The difference is as below:
Copy X : buyNewExpensivePhone() called
+ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ+ββββββββββββββ+
| Optional.isPresent() | true | false |
+ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ+ββββββββββββββ+
| findMyPhone(int phoneId).orElse(buyNewExpensivePhone()) | X | X |
+ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ+ββββββββββββββ+
| findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) | | X |
+ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ+ββββββββββββββ+
When optional.isPresent() == false, there is no difference between two ways. However, when optional.isPresent() == true, orElse() always calls the subsequent function whether you want it or not.
Finally, the test case used is as below:
Result:
Copy------------- Scenario 1 - orElse() --------------------
1.1. Optional.isPresent() == true (Redundant call)
Going to a very far store to buy a new expensive phone
Used phone: MyCheapPhone
1.2. Optional.isPresent() == false
Going to a very far store to buy a new expensive phone
Used phone: NewExpensivePhone
------------- Scenario 2 - orElseGet() --------------------
2.1. Optional.isPresent() == true
Used phone: MyCheapPhone
2.2. Optional.isPresent() == false
Going to a very far store to buy a new expensive phone
Used phone: NewExpensivePhone
Code:
Copypublic class TestOptional {
public Optional<String> findMyPhone(int phoneId) {
return phoneId == 10
? Optional.of("MyCheapPhone")
: Optional.empty();
}
public String buyNewExpensivePhone() {
System.out.println("\tGoing to a very far store to buy a new expensive phone");
return "NewExpensivePhone";
}
public static void main(String[] args) {
TestOptional test = new TestOptional();
String phone;
System.out.println("------------- Scenario 1 - orElse() --------------------");
System.out.println(" 1.1. Optional.isPresent() == true (Redundant call)");
phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
System.out.println(" 1.2. Optional.isPresent() == false");
phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
System.out.println("------------- Scenario 2 - orElseGet() --------------------");
System.out.println(" 2.1. Optional.isPresent() == true");
// Can be written as test::buyNewExpensivePhone
phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
System.out.println(" 2.2. Optional.isPresent() == false");
phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone());
System.out.println("\tUsed phone: " + phone + "\n");
}
}
Take these two scenarios:
CopyOptional<Foo> opt = ...
Foo x = opt.orElse( new Foo() );
Foo y = opt.orElseGet( Foo::new );
If opt doesn't contain a value, the two are indeed equivalent. But if opt does contain a value, how many Foo objects will be created?
P.s.: of course in this example the difference probably wouldn't be measurable, but if you have to obtain your default value from a remote web service for example, or from a database, it suddenly becomes very important.
Optional harnesses the type system for doing work that you'd otherwise have to do all in your head: remembering whether or not a given reference may be null. This is good. It's always smart to let the compiler handle boring drugework, and reserve human thought for creative, interesting work.
Without Optional, every reference in your code is like an unexploded bomb. Accessing it may do something useful, or else it may terminate your program wth an exception.
With Optional and without null, every access to a normal reference succeeds, and every reference to an Optional succeeds unless it's unset and you failed to check for that. That is a huge win in maintainability.
Unfortunately, most languages that now offer Optional haven't abolished null, so you can only profit from the concept by instituting a strict policy of "absolutely no null, ever". Therefore, Optional in e.g. Java is not as compelling as it should ideally be.
An Optional brings stronger typing into operations that may fail, as the other answers have covered, but that is far from the most interesting or valuable thing Optionals bring to the table. Much more useful is the ability to delay or avoid checking for failure, and to easily compose many operations that may fail.
Consider if you had your optional variable from your example code, then you had to perform two additional steps that each might potentially fail. If any step along the way fails, you want to return a default value instead. Using Optionals correctly, you end up with something like this:
return optional.flatMap(x -> x.anotherOptionalStep())
.flatMap(x -> x.yetAnotherOptionalStep())
.orElse(defaultValue);
With null I would have had to check three times for null before proceeding, which adds a lot of complexity and maintenance headaches to the code. Optionals have that check built in to the flatMap and orElse functions.
Note I didn't call isPresent once, which you should think of as a code smell when using Optionals. That doesn't necessarily mean you should never use isPresent, just that you should heavily scrutinize any code that does, to see if there is a better way. Otherwise, you're right, you're only getting a marginal type safety benefit over using null.
Also note that I'm not as worried about encapsulating this all into one function, in order to protect other parts of my code from null pointers from intermediate results. If it makes more sense to have my .orElse(defaultValue) in another function for example, I have much fewer qualms about putting it there, and it's much easier to compose the operations between different functions as needed.
This is part of JDK 9 in the form of method or, which takes a Supplier<Optional<T>>. Your example would then be:
return serviceA(args)
.or(() -> serviceB(args))
.or(() -> serviceC(args));
For details see the Javadoc, this post I wrote, or ticket JDK-8080418 where this method was introduced.
The cleanest βtry servicesβ approach given the current API would be:
Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
The important aspect is not the (constant) chain of operations you have to write once but how easy it is to add another service (or modify the list of services in general). Here, adding or removing a single ()->serviceX(args) is enough.
Due to the lazy evaluation of streams, no service will be invoked if a preceding service returned a non-empty Optional.
Starting with Java 9, you can simplify the code to
Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.flatMap(s -> s.get().stream())
.findFirst();
though this answer already contains an even simpler approach for JDK 9.
JDK 16 offers the alternative
Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.<Result>mapMulti((s,c) -> s.get().ifPresent(c))
.findFirst();
though this approach might be more convenient with service methods accepting a Consumer rather than returning a Supplier.