179. Debugging lambdas

There are at least three solutions when it comes to debugging lambdas:

  • Inspect a stack trace
  • Logging
  • Rely on IDE support (for example, NetBeans, Eclipse, and IntelliJ IDEA support debugging lambdas out of the box or provide plugins for it)

Let's focus on the first two since relying on an IDE is a very large and specific topic that isn't in the scope of this book.

Inspecting the stack trace of a failure that happened inside a lambda or a stream pipeline can be pretty puzzling. Let's consider the following snippet of code:

List<String> names = Arrays.asList("anna", "bob", null, "mary");

names.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList());

Since the third element from this list is null, we will get a NullPointerException, and the whole sequence of calls that defines the stream pipeline is exposed, as in the following screenshot:

The highlighted line tells us that this NullPointerException has occurred inside a lambda expression named lambda$main$5. This name was made up by the compiler since lambdas don't have names. Moreover, we don't know which element was null.

So, we can conclude that a stack trace that reports a failure inside a lambda or stream pipeline is not very intuitive.

Alternatively, we can try to log the output. This will help us debug a pipeline of operations in a stream. This can be accomplished via the forEach() method:

List<String> list = List.of("anna", "bob",
"christian", "carmen", "rick", "carla");

list.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);

This will give us the following output:

CARLA
CARMEN
CHRISTIAN

In some cases, this technique can be useful. Of course, we have to keep in mind that forEach() is a terminal operation, and so the stream will be consumed. Since a stream can only be consumed once, this can be an issue.

Moreover, if we add a null value to the list, then the output will become confusing again.

A better alternative consists of relying on the peek() method. This is an intermediate operation that executes a certain action on the current element and forwards the element to the next operation in the pipeline. The following diagram shows the peek() operation at work:

Let's see it in code form:

System.out.println("After:");

names.stream()
.peek(p -> System.out.println(" stream(): " + p))
.filter(s -> s.startsWith("c"))
.peek(p -> System.out.println(" filter(): " + p))
.map(String::toUpperCase)
.peek(p -> System.out.println(" map(): " + p))
.sorted()
.peek(p -> System.out.println(" sorted(): " + p))
.collect(Collectors.toList());

The following is an example of the output we may receive:

Now, let's intentionally add a null value to the list and run it again:

List<String> names = Arrays.asList("anna", "bob", 
"christian", null, "carmen", "rick", "carla");

The following output was obtained after adding a null value to the list:

This time, we can see that a null value occurred after applying stream(). Since stream() is the first operation, we can easily figure out that the error resides in the list content.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.145.82