Functional style of Java 8's Optional.ifPresent and if-not-Present?
For me the answer of @Dane White is OK, first I did not like using Runnable
but I could not find any alternatives.
Here another implementation I preferred more:
public class OptionalConsumer<T> {
private Optional<T> optional;
private OptionalConsumer(Optional<T> optional) {
this.optional = optional;
}
public static <T> OptionalConsumer<T> of(Optional<T> optional) {
return new OptionalConsumer<>(optional);
}
public OptionalConsumer<T> ifPresent(Consumer<T> c) {
optional.ifPresent(c);
return this;
}
public OptionalConsumer<T> ifNotPresent(Runnable r) {
if (!optional.isPresent()) {
r.run();
}
return this;
}
}
Then:
Optional<Any> o = Optional.of(...);
OptionalConsumer.of(o).ifPresent(s -> System.out.println("isPresent " + s))
.ifNotPresent(() -> System.out.println("! isPresent"));
Update 1:
the above solution for the traditional way of development when you have the value and want to process it but what if I want to define the functionality and the execution will be then, check below enhancement;
public class OptionalConsumer<T> implements Consumer<Optional<T>> {
private final Consumer<T> c;
private final Runnable r;
public OptionalConsumer(Consumer<T> c, Runnable r) {
super();
this.c = c;
this.r = r;
}
public static <T> OptionalConsumer<T> of(Consumer<T> c, Runnable r) {
return new OptionalConsumer(c, r);
}
@Override
public void accept(Optional<T> t) {
if (t.isPresent()) {
c.accept(t.get());
}
else {
r.run();
}
}
Then could be used as:
Consumer<Optional<Integer>> c = OptionalConsumer.of(
System.out::println,
() -> System.out.println("Not fit")
);
IntStream.range(0, 100)
.boxed()
.map(i -> Optional.of(i)
.filter(j -> j % 2 == 0))
.forEach(c);
In this new code you have 3 things:
- can define the functionality before the existing of an object easy.
- not creating object reference for each Optional, only one, you have so less memory than less GC.
- it is implementing consumer for better usage with other components.
By the way, now its name is more descriptive it is actually Consumer<Optional<?>>
Proper usage of Optional.ifPresent()
Optional<User>.ifPresent()
takes a Consumer<? super User>
as argument. You're passing it an expression whose type is void. So that doesn't compile.
A Consumer is intended to be implemented as a lambda expression:
Optional<User> user = ...
user.ifPresent(theUser -> doSomethingWithUser(theUser));
Or even simpler, using a method reference:
Optional<User> user = ...
user.ifPresent(this::doSomethingWithUser);
This is basically the same thing as
Optional<User> user = ...
user.ifPresent(new Consumer<User>() {
@Override
public void accept(User theUser) {
doSomethingWithUser(theUser);
}
});
The idea is that the doSomethingWithUser()
method call will only be executed if the user is present. Your code executes the method call directly, and tries to pass its void result to ifPresent()
.
How to write if optional is empty, call next method returning optional, if not return this non-empty optional several times in a functional style?
This kind of situation is what Optional.or
was added for. It was added in Java 9, so if you're still on Java 8 then you can't use it.
return validateItem(s.getItem())
.or(() -> validatePayment(req.getPayment()))
.or(() -> /*another one*/);
If you are stuck on Java 8, you could write a helper method
public static <T> Optional<T> firstNonEmpty(List<Supplier<Optional<T>>> supplierList) {
for (Supplier<Optional<T>> supplier : supplierList) {
Optional<T> value = supplier.get();
if (value.isPresent()) return value;
}
return Optional.empty();
}
And it use it like so:
return firstNonEmpty(
Arrays.asList(
() -> validateItem(s.getItem()),
() -> validatePayment(req.getPayment()),
() -> /*another one*/
)
);
How to execute logic on Optional if not present?
With Java 9 or higher, ifPresentOrElse
is most likely what you want:
Optional<> opt = dao.find();
opt.ifPresentOrElse(obj -> obj.setAvailable(true),
() -> logger.error("…"));
Currying using vavr or alike might get even neater code, but I haven't tried yet.
Java 8 How to return from a method if Optional is not present?
Seems like a use case for ifPresentOrElse
as in Java-9 :
obj.ifPresentOrElse(a -> {...}, () -> {logger.info(xxxx);return; });
Optional ifPresent otherwise call another function
You may use orElseGet
with a supplier to call some function which computes the value if the optional is empty. If a value is present, it returns the value, otherwise returns the result produced by the supplying function. In your case you have to pass Supplier<String>
. Moreover, your return type after unwrapping the Optional
should be a String
, not an Optional<String>
.
String totalBoxes = totalBoxesLastPage
.orElseGet(() -> nextToLastPage.flatMap(p -> p.locate(INVOICE_TOTAL_AMOUNT_BOXES))
.orElseThrow(IllegalStateException::new));
How to use Java 8 Optionals, performing an action if all three are present?
I think to stream the three Optional
s is an overkill, why not the simple
if (maybeTarget.isPresent() && maybeSourceName.isPresent() && maybeEventName.isPresent()) {
...
}
In my eyes, this states the conditional logic more clearly compared to the use of the stream API.
Call function in Java Optional ifPresent with OrElse
The base of the code is not compilable.
Stream#forEach
returnsvoid
, therefore you cannot performStream#filter
on that. Use eitherStream#peek
(please, read this) orStream#map
.Arrays.stream(fields)
.peek(field -> field.setAccessible(true))
.filter(field -> !field.get(oldRow).equals(field.get(newRow))
...Better use an appropriate method to avoid
Stream#map
/Stream#peek
as of Java 9 (big thanks to @Slaw's comment):Arrays.stream(fields)
.filter(field -> field.trySetAccessible() && !rowsAreEqual(field, oldRow, newRow))
...The method
Field#get
throws an exception that must be handled. The Stream API is not suitable for it, but you can create a wrapper to avoid the boilerplate.private static boolean rowsAreEqual(Field field, Row oldRow, Row newRow) {
try {
return field.get(oldRow).equals(field.get(newRow));
} catch (IllegalAccessException e) {
log.warn("Unexpected error", e);
return false;
}
}Arrays.stream(fields)
.peek(field -> !field.setAccessible(true))
.filter(field -> rowsAreEqual(field, oldRow, newRow))
...Notice, that the outer try-catch is not needed.
The argument of
Optional#isPresent
is aConsumer<T>
and you put therevoid
. Although theConsumer#accept
has avoid
return type, it must be called first. You need to pass the implementation represented by theConsumer<T>
and not its result:Arrays.stream(fields)
.peek(field -> field.setAccessible(true))
.filter(field -> rowsAreEqual(field, oldRow, newRow))
.findAny()
.ifPresentOrElse(
field -> newRow.updateACell(),
() -> newRow.udpateACell("new value"));
Java 8 optional: ifPresent return object orElseThrow exception
Actually what you are searching is: Optional.map. Your code would then look like:
object.map(o -> "result" /* or your function */)
.orElseThrow(MyCustomException::new);
I would rather omit passing the Optional
if you can. In the end you gain nothing using an Optional
here. A slightly other variant:
public String getString(Object yourObject) {
if (Objects.isNull(yourObject)) { // or use requireNonNull instead if NullPointerException suffices
throw new MyCustomException();
}
String result = ...
// your string mapping function
return result;
}
If you already have the Optional
-object due to another call, I would still recommend you to use the map
-method, instead of isPresent
, etc. for the single reason, that I find it more readable (clearly a subjective decision ;-)).
How to do Java Optional if present do something or else throw?
As Guillaume F. already said in the comments, you could just use orElseThrow
here:
Customer customer = customerRepository.findById(customer.getId())
.orElseThrow(() -> new Exception("customer not found"));
return customerRepository.save(customer);
By the way, avoid throwing Exception
, as that exception is too broad. Use a more specific type, like NotFoundException
.
Related Topics
Jtable with Titled Rows and Columns
How to Build Sources Jar with Gradle
Create a Autocompleting Textbox in Java with a Dropdown List
How to Format a String in an Email So Outlook Will Print the Line Breaks
Load Resource from Anywhere in Classpath
Intellij Show Javadocs Tooltip on Mouse Over
How to Intentionally Cause a Custom Java Compiler Warning Message
Using Jaxb to Unmarshal/Marshal a List<String>
How to Convert Current Date into String in Java
Java Error - Actual and Formal Argument Lists Differ in Length
Java Synchronized Block for .Class
Make Arraylist.Toarray() Return More Specific Types
Spring-Batch Without Persisting Metadata to Database
Eclipse Error: "Failed to Connect to Remote Vm"