Working with Streams and Optionals in Java 8

Felipe Juárez
5 min readOct 2, 2017

--

In the last couple of weeks I’ve been programming in Java 8. Because you know

Clients

With this new client, like many others, the main development is in Java (at least is Java 8, could be worst). To be honest, I’ve didn’t touch Java in almost 2 years and I had thought that the transition from Elixir to Java it would be painful, but Java 8 proved me that is not completely true (I’m still having some personal issues, in the way that Java does some things). Don’t get me wrong, I’m not saying that Java 8 is the future, but at least, still alive.

In one of my current tasks, I needed to parse an Excel file and converting into a collection of POJO’s. I struggled with this for a couple of hours. I wasn’t completely sure how to start this task, so I ended up starting with the things that I remember: JUnit and my old java syntax, and from there onwards.

After a couple of hours, I saw myself searching for the new features in Java 8 and refactoring the code that I wrote. The main things that I used immediately were the following:

Stream

As in the Java docs says, a Stream is:

A sequence of elements supporting sequential and parallel aggregate operations.

To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate)), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.

If you are an elixir developer this should be familiar is like the pipe operator |> It allows you to concatenate functions. The only thing is that the function should be in the Stream interface.

This element allows us to iterate the collection in a lazy way and apply functions that we need, for example a map function for transforming the collection into a desired element.

For example we can convert an array of string into an array of integers:

Or we can do a lot more, from iterate the list without explicit loops to apply a function only if the element isPresent (we will see more of this shortly)

We won’t get into the details of streams but, if you want to know more about it, you can check it in java8 stream tutorial examples

If you are very carefully and read the code you will find that our previous snippet has a slight error. In case you missed it, what happen if we change the value of the List to Arrays.asList("1", null, "3", "2", "1", null) and run the program?

Exception in thread "main" java.lang.NumberFormatException: null
at java.lang.Integer.parseInt(Integer.java:542)
at java.lang.Integer.parseInt(Integer.java:615)
at Numbers.lambda$main$0(Numbers.java:11)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at Numbers.main(Numbers.java:12)

Damn! Our little program crashes and the cause is: NumberFormatException. This happened because we are trying to parse a null value into an Integer and we all know that we can’t do that kind of operation.

We have a couple of workarounds for this, being the most used the conditional option, you know: if...else. But, after a couple of years doing pattern matching, this seems a dirty way for handling those scenarios, at least for me. Fortunately, after taking a closer look at the Stream documentation we discovered a couple of functions that returns an interesting class Optional.

Optionals

But, what does that means? We will see it in a moment.

Optional

If you know a little of Swift or you’ve heard about Ceylon, Optionals in Java are almost the same thing.

For the rest of us, this is the official definition of an Optional in Java:

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).

In summary, the Optional type is a way to avoid dealing with null objects. Let me show this with an example:

The output of the previous code is the following:

[Optional[1], Optional.empty, Optional[3], Optional[2], Optional[1], Optional.empty]

So, what now? Well with that convertion we can use a function from the Optional class for a safety parse into an Integer, as follows:

After running this program we should see that both asserts are correct. But, what happened here? Well if you put special attention in the line #20 you will see this return opt.map(Integer::parseInt).orElse(0);. Is in this line were the convertions occurs without and explicit if...else. If we read the documentation of the Optional class, we can see a function that take a Function interface and return the result of applying it to the current Optional element only if a value is present:

Optional map

That means that we don’t need an if...else condition for validating a null value. And, what about orElse function? Well, I think the name is explicit enough but, this function only apply when an Optional.empy value is passed. Is like a default value when nothing is present.

If you wan’t to know more about optionals you can check it here and here

Conclusions

As you can see the resulting programming is very interesting and we don’t need an explicit if...else. Instead, we can use the Optional class for removing that kind of conditions and just apply functions.

Is in this moment were I remember what I told you at the begining:

I had thought that the transition from Elixir to Java it would be painful, but Java 8 proved me that is not completely true

Is for this type of class and combinations I can say that Java 8 is not that painful and still alive. I still struggle with the imports, the verbosity of the language and a couple more things but, like I said: is personal.

For this post we are done, I hope you can find interesting and helpful. If you have any comments or any doubts please let me know.

See you next time. Good luck and have fun!

--

--

Felipe Juárez

Software Developer at MakingDevs, a competitive gamer currently playing SC2 and Clash Royale. I love beer, anime, manga, music my kids and my wife