Java 8 Features

Or how to make your life easier

Ariel Debrouvier
devartis

--

The release of Java 8 introduced a series of ground-breaking features for the language, that proved to be more than useful for the everyday life of Java developers.

Let’s explore some of these features and give examples of their use.

Lambda expressions

One of Java’s characteristics is its verbosity and the great amount of boilerplate code that is sometimes required for defining simple functions. This feature helps extensively on attacking this issue.

Lambda expressions are anonymous functions that are not bound to identifiers. They don’t need the creation of a class that contains them, they can be stored in variables and therefore be passed as arguments to other functions, set as instance variables, etc.

If we take for example the definition of a sum function:

public int add(int a, int b) {
return a + b;
}

It is equivalent to this lambda expression:

(a, b) -> a + b

It is noticeable that the amount of boilerplate code is considerably reduced. This is in part thanks to the type inference by context improvements in this Java version.

In the following code snippet, we can see how an alphabetical list sorting method can be simplified by taking advantage of the use of lambda expressions. The sort method receives an instance of a Comparator and it can be defined inline. The use of lambdas allows eliminating elements such as:

  • Annotations.
  • Function definition.
  • Variable types.
  • Return clause.
  • And more!
Lambda expressions examples.

In the last line, an example of the use of a Method Reference is shown, another feature included in Java 8. In this case, it basically means: for sorting the collection use as Comparator the compareTo method defined in the String class.

We can reference the following method types:

  • Instance methods
  • Static methods
  • Constructors

Functional Interfaces

Interfaces are one of Java’s main features for allowing polymorphism. Version 8 introduced the concept of Functional Interfaces, whose main characteristic is that they define a Single Abstract Method. They can be used in conjunction with the annotation FunctionalInterface, in order to enforce this definition at compile time.

Lambda expressions can be used to instantiate Functional Interfaces. Several generic useful interfaces were defined in the package java.util.function including functions, boolean predicates, suppliers, operators, among others. Next, an example of a binary operator that simply concatenates two Strings is shown. The operator is defined with a lambda expression and stored in a variable. It can be then used by calling the apply method.

BinaryOperator that concatenates two strings

In the Lambda expressions code snippet, the interface Comparator is an example of a functional interface: it has a single abstract method compare, which receives two elements of the same type and returns an integer that, according to the implementation, decides a metric for ordering elements.

Default methods

Prior to Java 8, all methods defined in an interface had to be abstract, meaning that they could not have an implementation and another class must implement the interface in order to define the functionality.

Let’s say we have an interface Vehicle, and a new functionality “fly” must be added to all the cars. This would require to implement the method fly() in every class that implements Vehicle. Default methods are a feature that adds the possiblity to define the implementation of a method on an interface, allowing backwards compatibilty with older classes that implemented that interface.

Default methods are identified with the keyword default. For the previous scenario, a default method fly() could be implemented in the interface Vehicle, adding the default behaviour for all the classes that implement this interface.

Vehicle interface implementing a default method.

Stream

Streams allow applying operations to series of elements as a pipeline.

The three main elements are: a source (the data to be processed), intermediate operators (filtering, mapping, ordering, etc.) and a terminal operator (e.g. collecting the processed data on a list).

In the following example an employee collection (e.g. a List) is converted into a stream by using its stream() method and it first filters those employees who are over the age of 20. Then, it maps them to their name, using a method reference to the corresponding getter. Finally, the names of the employees that satisfy the condition are collected on a list.

Another practical use would be performing arithmetical operations, such as an average. Lets say we want to calculate the average age of Junior employees, it can simply be done by filtering with the desired condition, and then collecting the results with an average:

There are different possible sources for a stream, some of them can be:

  • Collections.
  • Arrays.
  • Files.
  • Static factory stream methods such as Stream.of() or IntStream.range()

Stream operations offer the possibility to be executed in parallalel, allowing to improve the performance and execution time of operations that otherwise, would run sequentially. In order to use this feature, the method parallelStream() can be called on the source, and intermediate operations and a terminal operator can be chained, in the same way as with stream().

Optionals

Every Java programmer has crossed paths with the infamous NullPointerException. The introduction of the container object Optional brought a simple and useful way of managing null checks.

It allows creating a container from a value that could be null and provides a series of methods for checking if the value is present, retrieving the value itself or even obtaining defaults in case of absence of value.

For instance, a simple use case is for maps (also known as dictionaries in another languages), if a key doesn’t have an associated value, it returns null. Therefore we can construct an Optional from a nullable value, as in line 1 of the following example.

Summing up!

Java 8 has proved to be one of the most exciting updates to the language by introducing a series of more than useful functions and tools.

This article only scratched the surface of the mentioned characteristics and lots of things can be found in the docs or exploring by yourself!

Visit us!

--

--