Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tranforming Guava Optional with function that might return null

TL;DR

What is the best (i.e. most compact/readable) way of transforming a Guava Optional, using a function that might return null? (So that in that case, we should get an Optional.absent(), instead of an NPE.)

Bonus question: is there an advantage, in the Guava way, namely to throw NPE, if the transformer function returns null? (Instead of converting it to Optional.absent(), like in case of Java 8 Optionals.) Or is it just a shortcoming of the design?

Problem

As per the documentation of Guava Optional.transform:

Comparison to java.util.Optional: this method is similar to Java 8's Optional.map, except when function returns null. In this case this method throws an exception, whereas the Java 8 method returns Optional.absent().

Example

The following function is common to both examples:

public String methodThatMighReturnNull(boolean returnNull, Integer input) {
    return returnNull ? null : String.valueOf(input);
}

With Java 8 this does not throw an NPE, but returns an empty Optional:

Optional<Integer> input = Optional.of(42);
Optional<String> result = input.map(new Function<Integer, String>() {
    public String apply(Integer input) {
        return methodThatMighReturnNull(true, input);
    }
});

The following, similar code with Guava throws an NPE:

Optional<Integer> input = Optional.of(42);
Optional<String> result = input.transform(new Function<Integer, String>() {
    public String apply(Integer input) {
        return methodThatMighReturnNull(true, input);
    }
});

Possible alternatives

I have found/considered some alternatives, but, as described below, all are problematic:

  1. Wrap the return value into an Optional. Problem: too cumbersome, more difficult to read;

    Optional<Integer> input = Optional.of(42);
    Optional<String> result = input.transform(new Function<Integer, Optional<String>>() {
        public Optional<String> apply(Integer input) {
            return fromNullable(methodThatMighReturnNull(true, input));
        }
    }).get();
    
  2. Use if/else, by checking ifPresent. Problem: kind of kills the point of Optional. (We could just use the reference and check against null instead.)

    Optional<Integer> input = Optional.of(42);
    Optional<String> result;
    if (input.isPresent()) {
        result = fromNullable(methodThatMighReturnNull(true, input.get()));
    } else {
        result = Optional.absent();
    }
    
  3. Just applying the function, and wrapping it in fromNullable. (As found on GitHub, where the same problem was seemingly already brought up.) Problem: this is slightly different, than the original example. In this case, methodThatMighReturnNull could end up receiving null, while in the original case this was impossible.

    Optional<Integer> input = Optional.of(42);
    Optional<String> result;
    result = fromNullable(new Function<Integer, String>() {
        public String apply(Integer input) {
            return methodThatMighReturnNull(true, input);
        }
    }.apply(input.orNull()));
    
like image 523
Attilio Avatar asked Jun 06 '26 08:06

Attilio


1 Answers

Apart from the alternatives you listed in your question, here are two other possibilities:

  1. If you are on Java 8 or higher, starting from Guava 21.0, you have the Optional.toJavaUtil() instance method, which transforms your Guava Optional instance into a java.util.Optional one, and also the Optional.fromJavaUtil static method, which creates a Guava Optional instance from a java.util.Optional one.

    You can use them to let java.util.Optional take a function that invokes your method in its Optional.map method, applying its own semantics (it won't throw NullPointerException if the function returns null, but an empty java.util.Optional instead). Then, if you need it, convert this mapped java.util.Optional back to a Guava Optional via the Optional.fromJavaUtil method.

    Here's the code that shows this approach:

    Optional<Integer> input = Optional.of(42);
    java.util.Optional<String> temp = input.toJavaUtil()
        .map(i -> methodThatMighReturnNull(true, i));
    Optional<String> result = Optional.fromJavaUtil(temp);
    
  2. If you are not in Java 8 or higher yet (or if you are but don't like the previous approach), I would go for a variant of your 2nd alternative. For this, I would implement a transformNullSafe method with the semantics you need:

    public static <T, R> Optional<R> transformNullSafe(
            Optional<T> optional,
            Function<T, R> function) {
    
        if (optional.isPresent()) {
            return Optional.fromNullable(function.apply(optional.get()));
        } else {
            return Optional.absent();
        }
    }
    

    Usage:

    Optional<Integer> input = Optional.of(42);
    Optional<String> result = transformNullSafe(
        input, 
        new Function<Integer, String>() {
            public String apply(Integer i) {
                return methodThatMighReturnNull(true, i);
            }
        });
    
like image 112
fps Avatar answered Jun 08 '26 22:06

fps



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!