Comparing Java Optional vs C++ STL optional

Optionals in Java have been around for some time now, basically since the release of version 8. My other language of choice, C++, has received this feature in version C++17. Since I am currently in the process of writing some C++ code, I was curious how they were implemented there. Optionals are trying to solve a problem that is likely to plague any language. What shall a method or function return if there is no value? Or shall it not return anything but instead start crying like a petulant child and throw an exception?

As an introduction, let me dive a little bit into why we need optionals (or do we?) and compare two different implementations of this concept, one being java.util.Optional and the other C++ std::optional. I chose to compare these two language for several reasons:

  1. I work with Java in my day job, so I have a good idea of how it works there.
  2. As mentioned, C++ is one of the languages I know quite well too.
  3. The main reason: both optional implementations are add-on classes rather than language features. More on that later.

If you’re not interested in a story or what optionals are used for then feel free to skip directly to the code samples and the comparison.

Note: I will always use the term "function" throughout this post to be consistent. You may replace it with "method" in your head where applicable.

Why do we need optionals?

Return values can be a tricky thing. If your API is 100% crystal clear and there’s never the case that none of your methods never have to return "nothing" as a result and all 3rd party APIs you are using are perfect in this regard as well then consider yourself lucky. The more likely scenario, however, is that you’ve had to deal with null or -1 before. Both of which are common indicators that there is no result.

Without optionals, the way you can handle this situation depends on the return type. In some cases, the return type can be used to transport two types of results, one being the actual value and the other the information that there is no value. This means that the value returned from a function is subject to a specification. As a result, you will not be able to tell what the valid range of values and what the "there is no value"-value is, simply by reading the function’s definition. You must consult the API documentation in order to acquire that information. Now, I’m a big proponent of a good API documentation. However, history is my witness that many API’s are either poorly documented or not at all. It all depends on the popularity of the library that you are using and even popular libraries are victims to poor documentation. Another option is to write tests to find that out, but in my opinion this is time that should not be spent. It’s not your job to figure out an API’s return values.

Let’s use the following function as a contrived example:

int get_next_value() 
{
    if (have_next()) {
        return next();
    }
    return -1;
}

This function is used with an endless list of numbers and returns a number as long as the end of the list hasn’t been reached. The way this function is implemented, it uses -1 as an indicator that the end has been reached. In fact, many socket or file access APIs work like this. In this example, that immediately leads to a problem: the list cannot contain negative numbers or otherwise a value will conflict with the "there is no value"-value -1.

How about something that involves null? Take the following Java snippet for example.

Car findByModel(String model) {
    var found = database.executeQuery(...);
    if (found.hasResult()) {
        return Car.fromRow(found);
    }
    return null;
}

This is an imaginary database access function that executes a query and converts the row that was found into a POJO or returns null. Granted, in this case a seasoned programmer knows that Java objects can be null and null is a valid "value" for a reference to indicate no-value. It is perfectly legitimate to use it here.

How could this example be implemented with C++? C++ developers have the option to place complex objects on the stack instead of using pointers (basically what Java calls a reference). But such an object can only be a valid object, there is no nullptr in this case. As a workaround, you could add something like an is_null() function and evaluate the internal state. Yuck. Qt’s XML DOM implementation works this way, for example. Or its QDate and other date and time related classes. This way you can avoid using pointers and the nullptr value that comes with it. I’m sure we all were taught to shy away from pointers when possible. How about using a smart pointer like std::unique_ptr?

(Did I seriously ask that question?)

NO! Don’t do that!

We have two issues here:

  1. C++ objects on the stack cannot be null.
  2. Even if they could or if you chose to use pointers, a nullable return value is prone to result in a null-pointer access error. That is the case for C++ and Java.

The way I see it we are facing a main problem and a sub-problem.

The main problem is this: Just from reading the function definition you don’t know how the no-value case is handled.

The sub-problem is a result of that fact: You cannot force the proper handling of no-value by the compiler.

And if the user doesn’t know how no-value is treated it might likely cause one of those rare and pesky null-pointer exceptions or access violations that only happen once in a while, depending on the input, the day of the week, the sun’s intensity and a sack of rice tipping over on the other side of the planet. I’m sure we’ve all been there.

And this is where optionals come in. Java and C++ don’t have native support for optionals like C# or Kotlin do. Java and C++ use class wrappers to achieve a similar goal. Direct language support by the compiler can result in much more concise code and likely also has other benefits I’m too stupid come up with and too lazy to research right now. The fact that both languages I chose to examine here utilize classes as an optional implementation means that the compiler can only check for proper use of the optional class API.

Consider this little Java snippet:

Optional<String> getString() {
    return null;
}

That’s how you can still break the power of optionals in Java. Since java.util.Optional is merely a class instance it can also become null. The general consensus is to NOT do that, of course. But it’s an obvious drawback compared to an optional built into the language itself. C++ is safe in that regard because std::optional is supposed to be used as an object on the stack and as I have mentioned earlier, this cannot be null. You could, of course, do something like this:

std::optional<std::string>* get_string() {
    return nullptr;
}

If you do that though, you don’t deserve anything other than an endless tirade of illegal access errors.

With that "little" lecture on why optionals make sense out of the way (which of course nobody else other than me has ever done before), let’s get into comparing both implementations.

java.util.Optional vs. std::optional

All the sample code can be found on Github.

As outlined above, optionals shall be used as return values to indicate that there is no value and also to get rid of the tedious unhandled null-pointer exceptions. Let’s take the Java database example and first look at code that uses this function without optionals.

final var foundCar = findByModel("i30 N");
if (null == foundCar) {
    throw new NotFoundException();
}
foundCar.drive();

Pretty standard stuff. Shouldn’t be too hard to write, one would think. Keep in mind that this is a really simple example. There are use cases out there, where in literally 99% of all function invocations a value will be returned. It’s the edge cases where things blow up. Or, when time is scarce because deadlines a looming over a developer’s shoulders like Death himself, sharpening his scythe. Then programmers tend to omit error handling because, you know, it is expected to be fine all the time. Which it’s not and those errors will reveal themselves eventually.

Back to the topic. Let’s rewrite it with java.util.Optional.

Optional<Car> findByModel(String model) {
    var found = database.executeQuery(...);
    if (found.hasResult()) {
        return Optional.of(Car.fromRow(found));
    }
    return Optional.empty();
}

// Somewhere else...
final var foundCar = findByModel("i30 N");
if (foundCar.isEmpty()) {
    throw new NotFoundException();
}
foundCar.get().drive();

As you can see, the calling code changes by forcing you to handle the optional. Of course, you can still ignore it and try to access the value using get(). But by doing so, you are still reminded that there could potentially be no value. IDEs like IntelliJ highlight this by default. On first glance, this looks more cumbersome to use. For that, Java has some nice helper functions. In this example we throw an exception and that can be handled very elegantly.

final var foundCar = findByModel("i30 N").orElseThrow(NotFoundException::new);
foundCar.drive();

BAM! That’s even much nicer than the non-optional version. orElseThrow either returns the unwrapped value or throws an exception, all in one line. What about C++? Well, this is where optional doesn’t shine as bright. It is more limited in its functionality. But before I get ahead of myself, here’s the code.

std::optional<car> find_by_model(std::string model) {
    const auto found = database.execute_query(...);
    if (found.has_result()) {
        return std::make_optional(car.from_row(found));
    }
    return std::nullopt;
}

// Somewhere else...
const auto found_car = find_by_model("i30 N");
if (!found_car.has_value()) {
    throw not_found_exception{};
}
found_car.value().drive();
// Or, much nicer
found_car->drive();

This is basically everthing you can do with std::optional. It gives you the possibility of returning no value from a function without resorting to nullptr or any other tricks. The usage is not as convenient as Java though. You can check if there is a value with has_value() and retrieve this value with – you guessed it – value(). There is one more interesting function, value_or(), which I’ll get into later. Unlike Java though, C++ provides dereferencing operators like -> to get to the value without calling the value() function, as is also the case with iterators for example. But other than that, std::optional does not have the power of java.util.Optional. Just look at the documentation and see how many, or rather, how little functions there are. Java, on the other hand, has a plethora of functions, some of which I’ll be showing next.

Take a look at the C++ samples in the Github project to see how optionals can be created and also several use cases.

I’ve already covered the most simple and maybe most common case: test for a value and throw if none is available. What if you don’t want to throw but maybe use a default value? Well, both languages also got you covered.

final var defaultCar = new Car(...);
final var foundCar = findByModel("i30 N").orElse(defaultCar);
const auto default_car = car{...};
const auto found_car = find_by_model("i30 N").value_or(default_car);

This may not be the best example use case, but it shows how easy you can test for no value and replace it with a default value instead. This is much shorter than the traditional way with an if (null != foundCar) statement.

Java doesn’t stop there. If your logic depends on having an optional, you can also produce an optional instead of a "real" value. This is helpful in functions that don’t consume but produce data.

final var defaultCar = new Car(...);
final var foundCar = findByModel("i30 N").or(Optional.of(defaultCar));

Do you maybe want to perform some action if there is a value and if not, then just continue? Sometimes data is optional and you only execute code if it’s there. ifPresent() is your friend and only gets executed if the optional has a value. Otherwise it’s doing nothing.

findByModel("i30 N").ifPresent(this::tuneCar);

The equivalent of that would be the following.

final var foundCar = findByModel("i30 N");
if (foundCar.isPresent()) {
    tuneCar(foundCar.get());
}

And to throw one more C++ snippet in here: assuming the signature of the tuning function looks like this tune_car(car& pimp_my_ride) we can use the * operator as a shorthand to get to the value. Nicer than Java’s get() – although that is more explicit as to what is happening. But C++ coders should also easily understand the use of *.

const auto found_car = find_by_model("i30 N");
if (found_car.has_value()) {
    tune_car(*found_car);
}

Additionally, java.util.Optional also has a filter() function with which you can perfom conditional tests before committing to the value.

final var foundCar = findByModel("i30 N")
        .filter(car -> car.getProductionYear() == 2000)
        .ifPresent(this::tuneCar);

In that case only cars from 2000 will be tuned. You can also convert to another type by using the map() function. In my example this is useful if you want to convert from a database entity to a data transfer object.

final var foundCar = findByModel("i30 N")
        .filter(car -> car.getProductionYear() == 2000)
        .map(CarDto::fromEntity)
        .orElseThrow(NotFoundException::new);

See how you can chain functions to create a nice functional pipeline? Since this is not available in C++, how would that look with what we have right now?

const auto found_car = find_by_model("i30 N");
if (!found_car.has_value() || 2000 != found_car->production_year()) {
    throw not_found_exception{};
}

return make_dto(found_car);

I mean, it gets the point across and has worked for decades. But I have to admit, conditional statements like that have the potential to trip me up, force me to think harder about what it does. Unfortunately, I am notoriously bad deciphering more complex if statements and that is where I honestly welcome Java’s functional style.

When I switched jobs roughly a year ago, I was still in the camp that didn’t really like Java all that much. It was a nice enough language, but I still preferred C++. That has changed somehwat now. Java may have its flaws. However, through some important changes in version 8 and through libraries like Lombok it is actually a nice experience to write Java code nowadays. I still like C++, but a lot of that is a sentimental thinking because I’ve started my career on that language. It’s not bad, I’m not saying that. It excels in it own ways, but as I’ve written some C++ code recently, I have to say that collection handling is way more convenient using Java’s streams.

Anyway. I think this has been a pretty long piece and the only thing that makes it remotely original is the comparison to C++. Other than that, Java’s optionals are old news by now. It’s not the first time that I’ve compared several language features, so I think this fits right in. Maybe I’ll extend this to Kotlin in a future post, who knows.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.