Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An implementation of Optional for empty Strings

One of the best things about Optional is it saves all the boilerplate checking for null values in a long chain:

Optional.ofNullable(myService.getSomething())
    .map(secondService::fetch)
    .map(thirdService::fetchAgain)
    // And so forth...

At any point the Optional will jump onto the 'empty' track if map returns a null.

It would be great if something similar could be done for Strings instead of having to check them for String::isEmpty every time:

Optional.ofNullable(entity.getName())
    .filter(String::isEmpty)
    .map(Utils::performSomeOperation)
    .filter(String::isEmpty)
    .or(service::getMostCommonName)
    .filter(String::isEmpty)
    .orElse("Bob");

Something like this:

OptionalString.ofEmptyable(entity.getName())
    .map(Utils::performSomeOperation)
    .or(service::getMostCommonName)
    .orElse("Bob");

The key logic in Optional happens in ofNullable when it calls its check for value == null. Theoretically you could apply any sort of logic in there:

MagicalOptionalString(StringUtils::isNotBlank).ofEmptyable(entity.getName())
    .map(Utils::performSomeOperation)
    .or(service::getMostCommonName)
    .orElse("Bob");

However, Optional is final, preventing any straightforward way of extending this behaviour. So is there an existing, robust implementation of this out there already?

like image 630
Druckles Avatar asked Sep 19 '25 03:09

Druckles


1 Answers

Trying out a few things to resolve what you were aiming at, and realizing that I would second the thought from VGR as implementing such a use case is a lot of extra work as compared to using the existing methods.

Yet, few details that I could add to after spending some time looking over the implementations -

As a utility, you could implement a static implementation which verifies for both null and isEmpty condition for a string input and returns Optional accordingly. The code could look something like -

private static Optional<String> ofEmptyable(String string) {
    return isNullOrEmpty(string) ? Optional.empty() : Optional.of(string);
}

private static boolean isNullOrEmpty(String target) {
    return target == null || target.isEmpty();
}

this could then replace the usage of the ofNullable which specifically checks for null(the primary purpose of Optional).


Since the expectations in your problem statement were to actually handle the cases per method(map/or/orElse) call as in the optional, one approach similar to OptionalInt could be to implement a custom OptionalString as -

public final class OptionalString {

    private static final OptionalString EMPTY = new OptionalString();

    private final boolean isPresent;
    private final String value;

    private OptionalString() {
        this.isPresent = false;
        this.value = "";
    }

    private static OptionalString empty() {
        return EMPTY;
    }

    private boolean isPresent() {
        return isPresent;
    }

    private OptionalString(String value) {
        this.isPresent = true;
        this.value = value;
    }

    public static OptionalString of(String value) {
        return value == null || value.isEmpty() ? OptionalString.empty() : new OptionalString(value);
    }

    public OptionalString map(Function<? super String, ? extends String> mapper) {
        return !isPresent() ? OptionalString.empty() : OptionalString.of(mapper.apply(this.value));
    }

    public OptionalString or(Supplier<String> supplier) {
        return isPresent() ? this : OptionalString.of(supplier.get());
    }

    String orElse(String other) {
        return isPresent ? value : other;
    }

    public String getAsString() {
        return Optional.of(value).orElseThrow(() -> new NoSuchElementException("No value present"));
    }
}

which could be further implemented for your use case in the following manner -

String customImpl = OptionalString.of(entity.getName())
            .map(OptionalStringTest::trimWhiteSpaces) // OptionalStringTest is my test class name where 'trimWhiteSpaces' operation on String resides
            .or(service::getMostCommonName)
            .orElse("learning");
System.out.println(String.format("custom implementation - %s", customImpl));

where

private static String trimWhiteSpaces(String x) {
    return x.trim();
}

Note - Honestly, I couldn't find the rationale behind not having an OptionalString class upfront in the JDK (the reason why I am stating this is because I suspect there definitely must have been a thought behind it), I believe its just that the radius of my reach is much smaller and I would expect someone credible to add to the details here. IMHO, it seems more like almost all of what you desire is right there using the Optional<String> and which takes us back to the starting of the loop.

like image 106
Naman Avatar answered Sep 21 '25 16:09

Naman