Features of Java 8

Challenge Inside! : Find out where you stand! Try quiz, solve problems & win rewards!

Learn via video course

Java Course - Mastering the Fundamentals
Java Course - Mastering the Fundamentals
By Tarun Luthra
Free
star5
Enrolled: 1000
Java Course - Mastering the Fundamentals
Java Course - Mastering the Fundamentals
Tarun Luthra
Free
5
icon_usercirclecheck-01Enrolled: 1000
Start Learning

Overview

The revolutionary release of Java 8 in 2014 brought a huge upgrade to Programming in Java and a coordinated evolution of the JVM, Java libraries, and language. Java 8 features include Java streams which provide a mechanism for processing various kinds of data, lambda expressions that are not bound to any identifier and are anonymous, changes in Data and Time APIs, Stream API for files and data, interfaces that can be functional and so on.

Scope

  • Explanation of the Java 8 features with example.
  • Functional Interfaces, Lambda Expression, Stream API, Date/Time API, Optional Class, etc.

Introduction

On March 18, 2014, Java 8 was released. It brought several new features for productivity, security, improved performance, and ease of use which changed programming in Java. These features include the forEach() method in the Iterable interface, Date/Time API, improvements in Collection API and Concurrency API, Java Stream API, default and static method in Interfaces, and many more.

Features of Java 8

Let us look at all these features in Detail:

  • Functional Interfaces
  • Lambda Expression
  • Method Expression
  • Stream API
  • forEach() Method
  • Date Time API
  • Default and Static Method
  • Optional Class
  • Collectors Class
  • Parallel Array Sorting

Functional Interfaces

A functional interface in Java is an interface that contains only a single abstract (unimplemented) method. Along with a single abstract method, functional interfaces can have multiple default methods (interface methods with some default implementation, they can be overridden), static methods (methods in an interface having implementation, these can be used in implementing class but cannot be overridden as default methods), and methods of the object class. They can only exhibit one functionality.

Lambda Expressions can represent an instance of a functional interface to create an anonymous class and avoid bulky codes. Runnable, Callable, ActionListener, and Comparable are some of the examples of functional interfaces in Java.

@FunctionalInterface annotation is optional but it is a best practice to avoid the accidental addition of abstract methods to Functional Interfaces.

Lambda Expression

Lambda expressions are a means to create anonymous classes of functional interfaces easily. In other words, it is a concise and simple way of representing one method interface or Functional Interface using an expression. It is very handy while using the collection library as it helps to iterate, filter, and extract data from the collections like arrays, lists, etc.

Lambda Expression provides implementation to the only abstract method of the functional interface without defining the method again. It consists of 3 components, Argument list, Arrow-token -> and body.

Let us see an example using a Lambda expression where we create a functional interface and implement its abstract method using a Lambda expression.

Output:

In the above example, AddNumbers is a Functional Interface as it has only one method addition which takes two parameters n1 and n2 and returns an int. In class LambdaExpression, we have both the implementation with and without lambda expression, commented one is the implementation without lambda expression. Lambda Expression has three components,

  • Argument List - It can have zero, one, or many parameters as in the definition of the method. In the example above, n1 and n2 are arguments/parameters.
  • Arrow-Token - -> symbol is used to separate parameters from the body in a lambda expression.
  • Body - This can be defined without curly brackets if we have only one line of code and with curly brackets {//...} for multiple lines, as in our method.

Using lambda expression in Java 8, collection processing can be done using multiple threads. Previously, to perform multithreading, programmers were to write long codes. Now, using lambda expression and Stream API, we can pass the processing logic of elements into methods provided by collections, and now the collection is accountable for the parallel processing of elements.

Method References

Method references are a shorter and more readable way of lambda expression for only calling existing methods. They are compact, easy-to-read references that can be used to replace a single method of the lambda expression. Besides, in the method reference scope, resolution operator(::) is used to separate the method name from the class name.

For Example:

can be rewritten with method references as below, without passing any parameters.

Let us use this to print elements of ArrayList.

Output:

Method references are of 4 types:

1. Reference to the Static Method defined in the Class

As we are referring to a static method, we don't require an instance of a class.

Syntax:

2. Reference to an Instance Method of a particular object

We use the object of a class to refer to its instance methods.

Syntax:

3. Reference to an Instance Method of an arbitrary object of a particular type

Multiple objects of the same type are called the same method using method references.

Syntax:

4. Reference to the Constructor

Referring to a constructor method with a new keyword.

Syntax:

Stream API

Stream API is one of the important features of Java 8. All the classes and interfaces of Streams API in Java have located in java.util.stream package. Streams API supports iteration of the elements of collections, where we can process these elements, perform operations on them, and store the output. Streams are functional i.e. the changes made to elements do not modify the source.

Operations on Streams are of two types:

  • Intermediate Stream operations - these return a new stream. For example, filter() operation returns a stream of elements satisfying the required condition, sort() operation first sorts and returns a stream of elements, etc.
  • Terminal operations - these give the final output for a stream. For instance, the collect() method collects a stream of elements returned by intermediate operations into a list or similar collection, forEach() method iterates through each element of a collection, etc.

Let us take an example of a list, sort its elements, and store it in another list using Streams in Java.

Output:

  • In the example, we create a list of names. Let us say that we want another list with names in sorted order and all the names should end with 'a'.
  • So firstly we create a List called namesEndingWith_a. We call stream() on names List.
  • filter(name -> name.charAt(name.length()-1) == 'a') returns stream of elements that have last character as 'a'.
  • We sort the filtered stream using sorted() method and collect(Collectors.toList()) copies all the elements returned by sorted().
  • As we can see in the output all the names are sorted and end with 'a'. forEach() method takes each element from List and prints them using System.out::println which is a reference method.

forEach() Method

The forEach() method is defined as default in Iterable Interface is used to iterate through elements. Collection Interface extends Iterable Interface thus, classes implementing Collection Framework like Arrays, Lists, Stacks, etc. also extend Iterable Interface.

The forEach() method facilitates functional programming in Java, i.e., we can use it to transverse elements/objects in a functional style or while working with stream API. It takes only a single Consumer object as an argument like lambda expression, method reference, etc.

Syntax:

forEach() method takes action to be performed on each element of a Collection as the parameter.

Let us take an example of forEach() method to display data:

Output:

In the instance above, we insert some random months and numbers of days in the respective month into HashMap. Using the forEach() method, we iterate through each element, and check if the number of days is 31; if yes, we print the month. Thus, we can not only transverse, we can filter elements, and perform some action on them using the forEach() method.

Note: We have another method forEachOrdered(), which is used while using parallel streams. This method ensures the order of elements is the same as that of the source.

Date Time API

Existing Date and Time APIs in Java were not thread-safe, there was a lack of consistency as java.util and java.sql both packages define Date class, and it didn't have support for timezone, so developers had to write an additional logic for timezone and thread-safety.

Java 8 introduced java.time package. The new Date and Time APIs are immutable and thus thread-safe, follow consistent domain models for the date, time, duration, and periods as well as support Local and Zonal Date/Time APIs.

New Date and Time API have two important classes among them:

  1. Local - This class provides simple Date and Time operations in the Local Time Zone.
  2. Zoned - It contains Time zone-specific Date/Time and its operations.

Let us take an example to see various methods of Local and Zonal classes, like storing system date or time, performing some operations on them, etc.

Output:

  • LocalDate datatype stores date, LocalDate.now() returns System's Date.
  • Similarly, LocalTime datatype stores time in hours, minutes, and seconds, LocalTime.now() returns the current time.
  • todaysTime.getHour() and todaysTime.getMinute() returns value of hour and value of minute in LocalTime todaysTime respectively.
  • parse() method converts String to LocalDateTime. Its Format is yyyy-mm-ddThh:mm.
  • LocalDateTime stores date and time both in the same variable date_Time. LocalDate.now().plusDays(1) adds one day to the date i.e., 28th January became 29th January in our example. We can add more than one day, month, etc to the date. In 22-01-29T09:45:00, 22-01-29 represents date in format yyyy-mm-dd and 09:45:00 represents time in format hh-mm-ss. T in between date and time separates both and denotes the time value in the LocalDateTime variable.
  • date_Time.getDayOfWeek() returns day on particular date. In our example it is SATURDAY.
  • We can also use different Zones and ZonedDateTime. ZoneId stores the Zone we want to work upon, like Europe/London, Europe/Paris, Asia/Tokyo, etc. ZonedDateTime takes date and time and ZoneId as arguments.

Default and Static Methods

From Java 8 onwards, interfaces can have declared methods along with abstract methods. Methods are declared using the default keyword. We do not require to Override these methods; they can directly be used. In case we want a customized implementation, we can change the behavior of the method by Overriding it.

Static methods are declared using the static keyword, they are just like default methods defined in the interface itself. Static methods are available only through and inside an interface. They cannot be overridden by implementing classes. In order to use static methods, the Interface name should be initiated with it. They are only part of the interface.

Let us take an example:

Output:

Maths interface has a default method max, an abstract method min and a static method average. max method can be directly used by the objects of class InterfaceDefault, as it implements Maths. Abstract method min needs to be overridden in the class. The static method average can be used with an Interface Maths only.

Optional Class

Optional class is present in Java.util package. It is a public final class used to avoid NullPointerExceptions in Java Applications. The optional class contains methods that provide easy and concise ways to check if some value is null and perform an action if it is null.

Example:

Output:

A few of the useful methods of Optional class are described below:

  • Optional.ofNullable() - if given object is null it returns an empty Optional, else it returns Non-empty Optional. As in our example message is null, it will return empty Optional.
  • StringOptional.isPresent() - It returns true if Optional is Non-empty, else returns false. Thus, it prints Message is Empty.
  • In the case of GoodMorning, it will be Non-Empty Optional. GoodMorning.ifPresent() - if Optional is Non-Empty it can perform some action to it. In our case, we simply print the value of Optional i.e. GoodMorning.
  • StringOptional.orElse() - it assigns a value only if the given Optional is Empty.

Collectors Class

The Collectors class extends the Object class and is located in java.util.stream package. It is a final class. It provides various methods for reduction operations like accumulating elements into collections, summarizing them according to some criteria, etc.

Output:

intList contains a few integers, we create a stream(), we filter elements, and if they are divisible by 4, we include them in the result. .collect(toList) creates a new List, and stores each element from the intList to a result that satisfy the filter condition. In the example 4, 16, and 100 are divisible by 4 and thus we store them to result and print them. Instead of toList(), we can also use toSet, toMap, toCollection (here we need to specify the type of collection i.e LinkedList, TreeSet, etc.), etc. as per the requirement.

Parallel Array Sorting

The parallelSort() method is introduced in the Array class of java.util package. It uses the concept of multithreading in order to sort the array faster. It first goes on dividing the array into subarrays, these subarrays are sorted individually by multiple threads and then merged together.

Output:

parallel array sorting

Conclusion

  • Java 8 brought many features and libraries that improved programming in Java.
  • Functional interfaces that have only one abstract method. Functional interfaces can be used to implement lambda expressions.
  • Lambda expression provides a simple and compact way to implement an abstract method of functional interface. It consists of three components, argument, arrow-token, and body.
  • Method references are a concise way of calling existing methods instead of using a lambda expression.
  • Stream API introduced in Java 8 is useful for iterations, filtration, performing operations, and storing of the elements of a collection. Stream operations are of two types, Intermediate Stream operations and Terminal Operations.
  • foreach() method provides support for iteration of elements of Collections like Array, ArrayList, Stack, Queue, etc.
  • Java 8 also brought many new methods for Date and Time operations in Java. Two such important classes are Local and Zoned. Using Local Date and Time, we can perform operations on system date and time. Java has more than 40 zones, from which programmers can choose and use as per the requirement.
  • Interfaces can now have method implementations. Methods declared using the default keyword contains a pre-defined implementation, but they can be overridden by implementing classes.
  • Static methods are also declared in Interfaces like default methods. But they cannot be overridden or changed.
  • Optional class provides ways to avoid NullPointerExceptions. We can check if the value is null, provide a default value, and perform some action.
  • Collector class facilitates the collection of a set of elements and stores them in Lists, Sets, Maps, etc. using stream().
  • Parallel array sorting uses the concept of multithreading and sorts the array faster.