Java joined the Funtional Programming game with the release of Java 8 back in 2014 by Oracle. Consequently, this was considered to be a revolutionary and a long awaited release for Java.

In this post, we will quickly go through few features offered by Java 8.

1. Stream Processing

Almost in every Java application we need to group data and process data. Java Collections API is heavily used in these cases, however, there are few limitations here –

  1. The size of the collection gradually hampers the processing performance of the collection.
  2. We need to manually iterate over the Collections.

Java 8 Stream API offers better solution for grouping and processing data by taking care of the above limitations. With Stream, we can process data in a declarative and cleaner way.

Let us try to get a better understanding of the feature by the following code snippet for example –

// menu is a List<Dish> with few Dish objects

// Iterate over menu and filter dishes with less than 400 calories in 'lowCaloricDished'
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d: menu) {
    if(d.getCalories() < 400) {
        lowCaloricDishes.add(d);
    }
}

// Sort the items of 'lowCaloricDishes' with calory value
Collections.sort(lowCaloricDishes, new Comparator<Dish>({
    public int compare(Dish d1, Dish d2) {
        return Integer.compare(d1.getCalories(), d2.getCalories());
    }
});

// Create a new list 'lowCaloricDishesName' with dish names
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d: lowCaloricDishes) {
    lowCaloricDishesName.add(d.getName());
}

In above example, we used the Java Collections API to process menu and other lists. We filtered the menu list with a specific condition, sorted the filtered list and then, finally, mapped the name value from Dish objects. Now, see how we can do the same thing with Stream API –

List<String> lowCaloricDishesName = menu.stream()
                                        .filter(d -> d.getCalories()<400)
                                        .sorted(comparing(Dished::getCalories))
                                        .map(Dish::getName)
                                        .collect(toList());

As we can see, the latter example is much more cleaner, we just specify what we want to achive.

Stream operations have two important characteristics –

  1. Internal Iteration – In Collections we need to do the iteration manually, whereas Stream operations do the iteration behind the scenes, internally. For example, in the above code snippet, we did not use any loop for iteration.
  2. Pipelining – Many Stream operations return a stream themselves which allow operations to be chained. In above example, filter, sorted, map operations returned Stream and a chained pipeline is created.

2. Lambda Expression

Lambda Expression was introduced in Java 8. Lambda Expression is a consice anonymous function which can be passed around. It does not have a name, but it has a list of parameters, a body, a return type and possibly list of exceptions. Let’s see an example –

Comparator<Apple> byWeight = new Comparator<Apple>() {
    public int compare(Apple a1, Apple a2) {
        return a1.getWeight().compareTo(a2.getWeight());
    }
}

We can create the same Comparator using Lambda Expression in following way –

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

A lambda has three parts – 1. A list of parameters – The two apples a1 and a2. 1. An arrow – The arrow -> Separates list of parameters from the body. 1. The body – The compare operation.

3. Optional

NullPointerException is one of the most known exception among Java developers. We get this exception when there is an absence of value in the variable, which is very common. Thus, in order to avoid this exception, we must go through quite bit of code checking. Let’s look at the following example now –

public String getCarInsuranceName(Person person) {
    if(person != null) {
        Car car = person.getCar();
        if(car != null) {
            Insurance insurance = car.getInsurance();
            if(insurance != null) {
                return insurance.getName();
            }
        }
    }
}

Java 8 introduces an Optional to minimize the null checking verbosity. It’s a wrapper around an object when there is chance that the value might be null or absent. Optional means it can give a Dish object or nothing. Let’s now rewrite the above code snippet using Optional –

public Optional<String> getCarInsuranceName(Person person) {
    Optional<Person> optPerson = Optional.of(person);
    Optional<String> optInsurancename = optPerson.map(Person::getCar)
                                            .map(Car::getInsurance)
                                            .map(Insurance::getName);
    return optInsurancename;
}

Default methods for interfaces and new Date and Time API features also been added in Java 8 along with other features.

Previous ArticleNext Article

Leave a Reply

Your email address will not be published. Required fields are marked *