Java 8 Features

Back to home
Logicmojo - Updated Jan 11, 2023


Early in 2014, Java 8 was released. This course includes examples of major Java 8 technologies such as lambda expressions, Java streams, functional interfaces, default methods, and updates to the date-time API.

Functional Interface

An interface with precisely one abstract method is known as a functional interface.

//Optional annotation
public interface MyFirstFunctionalInterface {
    public void firstWork();

It's worth noting that a functional interface can exist without the @FunctionalInterface annotation. Its sole purpose is to instruct the compiler to enforce a single abstract method within the interface.

We can also add as many default methods to the functional interface as we need because default methods are not abstract.

Another thing to keep in mind is that if a functional interface overrides one of java.lang.public Object's methods, that does not count toward the interface's abstract method count because Any implementation of the interface, whether from java.lang.Object or elsewhere, will have an implementation.

Creating objects with lambda expressions is one of the most enticing elements of the functional interface. Although an anonymous class can be used to create an interface, the code is difficult to understand. Consider the following scenario:

For example, given below is a perfectly valid functional interface.

public interface FunctionalInterface_one
    public void firstInt_method();
    public String toString(); //Overridden from Object class
    public boolean equals(Object obj); //Overridden from Object class

Lambda Expressions

Many of us who have dealt with other popular programming languages like Scala are familiar with lambda expressions. A Lambda expression (or function) in the Java programming language is just an anonymous function, that is, a function that has no name and is not connected to an identifier.

Lambda expressions are written exactly where they are required, usually as a parameter to another function.


A few basic syntaxes of lambda expressions are:

(parameters) -> expression
(parameters) -> { statements; }
() -> expression

A typical lambda expression example will be like this:

//This function takes two parameters and return their sum
(x, y) -> x + y  

Please keep in mind that the procedure may be used in different places depending on the kind of x and y. Parameters can also match to int, Integer, or String. It will either add two integers or concatenate two texts depending on the circumstances.

Let’s implement a program that demonstrates Lambda Expressions.

Implementation in Java

interface MyInterface
     void abstract_func(int x,int y);
     default void default_Fun()
         System.out.println("This is default method");
class Main
     public static void main(String args[])
        //lambda expression
        MyInterface fobj = (int x, int y)->System.out.println(x+y);
        System.out.print("The result = ");

Rules for writing lambda expressions

  1. 1. There can be zero, one, or more parameters in a lambda expression.

  2. 2. The type of the arguments can either be specified explicitly or deduced from the context.

  3. 3. Multiple arguments are separated by commas and must be enclosed in obligatory parenthesis. An empty set of parameters is represented by empty parentheses.

  4. 4. It is not necessary to use parentheses when there is only one argument and its type can be inferred.

  5. 5. The lambda expressions can have zero, one, or more statements in the body.

  6. 6. Curly brackets are not required if the body of the lambda expression has only one statement, and the anonymous function's return type is the same as the body expression's. When the body contains multiple statements, they must be contained in curly brackets.

Default Methods

Non-abstract methods can now be added to interfaces in Java 8. Default methods must be declared for these methods. In Java 8, default methods were added to enable lambda expression capability.

Default methods allow us to add new functionality to our libraries' interfaces while maintaining binary compatibility with code written for previous versions of those interfaces.

Let’s understand with an example:

public interface Moveable {
    default void move(){
        System.out.println("I am moving");

The Moveable interface specifies the move() method and includes a default implementation. If a class implements this interface, it is not required to implement its own move() function. It can call instance.move() directly.

public class Animal implements Moveable{
    public static void main(String[] args){
        Animal tiger = new Animal();

If a class wants to alter the functionality of the move() method, it can override the method and give its own custom implementation.

Java 8 Streams

Another notable change was the introduction of the Java 8 Streams API, which provides a mechanism for processing a set of data in a variety of ways, including filtering, transformation, and any other method that an application would find helpful.

In Java 8, the Streams API enables a new type of iteration in which we specify the collection of items to be processed, the operation(s) to be performed on each item, and the location where the output of those actions should be stored.

Stream API Example

In this case, items is a collection of String values, and we want to get rid of the ones that start with a certain prefix text.

List<String> items = ...; //Initialize the list
String prefix = "str";
List<String> filteredList =
          .filter(e -> (!e.startsWith(prefix)))

Items can be found here. The stream() method specifies that we want to use the Streams API to collect data for the items collection process.

Java 8 Date/Time API Changes

The new Date and Time APIs/classes (JSR-310), often known as ThreeTen, have fundamentally altered how we handle dates in Java programmes.

Date Classes

Date class has even become obsolete. The new classes intended to replace Date class are LocalDate, LocalTime and LocalDateTime.

  1. 1. The LocalDate class represents a date. There is no information of a time or time-zone.

  2. 2. The LocalTime class represents a time. There is no information of a date or time-zone.

  3. 3. A date and time are represented by the LocalDateTime class.There is no information of a time-zone.

If we wish to use the date functionality with timezone information, Lambda provides us with three additional classes, OffsetDate, OffsetTime, and OffsetDateTimeTime, which are comparable to the above ones.

The timezone offset might be written as "+05:30" or "Europe/Paris." Another class, ZoneId, is used to do this.

LocalDate localDate =;
LocalTime localTime = LocalTime.of(12, 20);
LocalDateTime localDateTime =; 

OffsetDateTime offsetDateTime =;
ZonedDateTime zonedDateTime ="Europe/Paris"));

Method References

The Method reference feature, which was introduced in Java 8, is a shorthand notation for calling a Functional Interface method via Lambda Expressions. As a result, whenever you use a Lambda Expression to refer to a method, you can substitute it with a method reference.

Example of Method Reference.

import java.util.Optional;   
interface interface_default {
       void display();
class derived_class{
    public void classMethod(){  
            System.out.println("Derived class Method");  
class Main{
     public static void main(String[] args){
        derived_class obj1 = new derived_class();
        interface_default  ref = obj1::classMethod; 

We have an interface "interface default" with an abstract method "display ()" in this programme. Then there's the "derived class" class, which has a public method called "classMethod" that prints a message.

We have an object for the class in the main function, and then a reference to the interface through obj1 that references a class method "classMethod" (class object). The contents of classMethod are now displayed when the abstract method display is called by interface reference.

forEach() Method In Iterable Interface

The java.lang interface now includes a "forEach" function in Java 8. Iterable that can iterate over the elements of the collection. "forEach" is a default method defined by the Iterable interface. It is used to iterate elements by Collection classes that extend the Iterable interface.

You can send Lambda Expression as an argument to the "forEach" method, which takes the Functional Interface as a single parameter.

Example of the forEach() method.

public class Main {  
     public static void main(String[] args) {  
        List<String> subList = new ArrayList<String>();  
        System.out.println("------------Subject List--------------");  
        subList.forEach(sub -> System.out.println(sub));  

So we have a subList, which is a collection of subjects. The forEach method, which takes a Lambda Expression to print each element, is used to display the contents of the subList.

Optional Class

In Java 8, the "java.util" package added an optional class. The public final class "Optional" is used to handle NullPointerException in a Java application. You can give alternate code or values to run using Optional.To avoid a nullPointerException, this option decreases the amount of null checks required.

You can use the Optional class to prevent the programme from crashing and terminating unexpectedly. The Optional class has methods for checking the presence of a value for a given variable.

The following program demonstrates the use of the Optional class.The following program demonstrates the use of the Optional class.

import java.util.Optional;   
public class Main{   
   public static void main(String[] args) {   
        String[] str = new String[10];   
        Optional<String>checkNull =  
        if (checkNull.isPresent()) {   
            String word = str[5].toLowerCase();   
         } else 
           System.out.println("string is null");   

To verify if the string is null in this application, we use the Optional class's "ofNullable" attribute. If this is the case, the user will receive the appropriate notification.

With this article at Logicmojo, you must have the complete idea of java8 features.