Functional Style of Java 8's Optional.Ifpresent and If-Not-Present

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:

  1. can define the functionality before the existing of an object easy.
  2. not creating object reference for each Optional, only one, you have so less memory than less GC.
  3. 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 Optionals 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

  1. The base of the code is not compilable. Stream#forEach returns void, therefore you cannot perform Stream#filter on that. Use either Stream#peek (please, read this) or Stream#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))
    ...
  2. 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.

  3. The argument of Optional#isPresent is a Consumer<T> and you put there void. Although the Consumer#accept has a void return type, it must be called first. You need to pass the implementation represented by the Consumer<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



Leave a reply



Submit