Introduction To Java 8

Introduction

 
Java, the most widespread programming language for software development, has been brought to a new phase after introducing some new features. In this article, I will be explaining about new features in Java 8. If you are thinking that it's just yet another update of Java --  then “NO” it is much different from older versions of Java 1.0 to 1.7.
 
We got three important new features which are listed as,
  1. Functional Programming
  2. Lambda Expressions
  3. Stream API
Including these three features, Java 8 includes many more features which are also listed:
  1. Default and static Methods inside Interface.
  2. forEach() method in interface(iterable)
  3. Optional Class
  4. Method References
  5. Date Time API
  6. Nashorn JavaScript Engine
  7. Functional Interface
  8. Collection API Improvements
  9. String Joiner

Explanation

 
For years, we have been implementing our code (programming logic) with objects, but here the main thing is behavior of the object; i.e functionality of the object. For this we have two techniques,
  • Imperative Programming : specifies what to do and how to do it.
  • Declarative Programming : specifies what to do.
Here are some brief descriptions of all the features listed above, detailed explanations will be shared for each feature shortly with you.
  • Functional Programming: As explained earlier, for  years we have been using objects in our code irrespective of the language used for software development, here Java has come up with a feature to use functional programming; i.e instead of focusing on objects here we focus on functions .
  • Functional Interface: It is an interface that contains only one abstract method. It can have only one functionality to exhibit. It can be easily recognized by the @FunctionalInterface annotation.
Here, lambda expressions can be used to represent instances of functional Interface.
 
EmployeeController.java
  1. public class EmployeeController {  
  2.     public static void main(String[] args) {  
  3.         List < Employee1 > list = Arrays.asList(new Employee1(1"ABC1""UP1"10000), new Employee1(2"ABC2""UP2"2000), new Employee1(3"ABC3""UP3"30000), new Employee1(4"ABC4""UP4"40000), new Employee1(5"ABC5""UP5"50000));  
  4.         Employee1[] emp = (Employee1[]) list.toArray();  
  5.         Predicate < Employee1 > predicate = e - > e.getSalary() > 10000;  
  6.         Arrays.stream(emp).filter(predicate).forEach(System.out::println);  
  7.     }  

Employee.java
  1. public class Employee {  
  2.     private int id;  
  3.     private String name;  
  4.     private String location;  
  5.     private double salary;  
  6.     public Employee1(int id, String name, String location, double salary) {  
  7.         super();  
  8.         this.id = id;  
  9.         this.name = name;  
  10.         this.location = location;  
  11.         this.salary = salary;  
  12.     }  
  13.     public int getId() {  
  14.         return id;  
  15.     }  
  16.     public void setId(int id) {  
  17.         this.id = id;  
  18.     }  
  19.     public String getName() {  
  20.         return name;  
  21.     }  
  22.     public void setName(String name) {  
  23.         this.name = name;  
  24.     }  
  25.     public String getLocation() {  
  26.         return location;  
  27.     }  
  28.     public void setLocation(String location) {  
  29.         this.location = location;  
  30.     }  
  31.     public double getSalary() {  
  32.         return salary;  
  33.     }  
  34.     public void setSalary(double salary) {  
  35.         this.salary = salary;  
  36.     }  
  37.     @Override  
  38.     public String toString() {  
  39.         return "Employee1 [id=" + id + ", name=" + name + ", location=" + location + ", salary=" + salary + "]";  
  40.     }  

The code will produce output as,
  1. Employee[id = 2, name = ABC2, location = UP2, salary = 20000.0]  
  2. Employee[id = 3, name = ABC3, location = UP3, salary = 30000.0]  
  3. Employee[id = 4, name = ABC4, location = UP4, salary = 40000.0]  
  4. Employee[id = 5, name = ABC5, location = UP5, salary = 50000.0
Lambda Expressions
 
It means a block of code that you can pass around and we can execute it later, it uses a new -> operator, can be made of zero, one or multiple lines. It is used to remove the boilerplate code and focus on the main thing.
 
Syntax to use lambda expressions : lambda operator -> body
 
Example
 
LambdaDemo.java
  1. interface A {  
  2.     void show();  
  3. }  
  4. public class LambdaDemo {  
  5.     public static void main(String[] args) {  
  6.         A obj; //reference to interface  
  7.         //Lambda Expression without Parameter  
  8.         obj = () - > {  
  9.             System.out.println("Hello");  
  10.         };  
  11.         obj.show();  
  12.         //Lambda Expression with Parameter  
  13.         obj = (name) - > {  
  14.             System.out.println("Hello " + name);  
  15.         };  
  16.         obj.show("Aman");  
  17.     }  

Java Stream API
 
A Stream is a sequence of elements from the source (collections) that supports parallel and sequential aggregate operations. A stream pipeline consists of source, followed by zero or more intermediate operations and a terminal operation.
    1. Source - Filter - Collect
    2. Source - Filter - Map - Collect
Example
 
StreamAPIDemo.java
  1. public class StreamAPIDemo {  
  2.     public static void main(String[] args) {  
  3.         List < Integer > list = new ArrayList < > ();  
  4.         list.add(3);  
  5.         list.add(8);  
  6.         list.add(9);  
  7.         List < Integer > streamlist = list.stream() //source  
  8.             .filter(x - > x % 2 == 0//intermediate operation  
  9.             .collect(Collectors.toList()); //terminate operation  
  10.         System.out.println("Original List : " + list);  
  11.         System.out.println("Filtered List : " + streamlist);  
  12.     }  

This code will provide output as,
 
Original List : [3, 8, 9]
Filtered List : [8]
 
Default and static Methods inside Interface
 
In earlier versions of Java, we can only declare functions inside an Interface but with the introduction of Java 8 era, we can even define methods inside an Interface and this can be achieved by using two keywords of Java,
  1. Default and
  2. Static
Default Methods in Interfaces
 
Before Java 8, Interfaces can only have abstract methods and final variables, to overcome this Java 8 introduced default methods which allows the interface to have methods with implementation.
 
Example
 
InterfaceDefaultMethod.java
  1. interface DefaultMethod {  
  2.     default void B() {  
  3.         System.out.println("method B");  
  4.     }  
  5. }  
  6. public class InterfaceDefaultMethod implements DefaultMethod {  
  7.     public static void main(String[] args) {  
  8.         DefaultMethod dm = new InterfaceDefaultMethod();  
  9.         //object of class through Interface  
  10.         dm.B();  
  11.     }  

This will produce an output as: method B
 
Static Methods in Interfaces
 
Static methods contain complete definition of the function, since the definition is complete and methods are static, they cannot be overridden.
 
InterfaceStaticMethod.java
  1. interface StaticMethod {  
  2.     static void B() {  
  3.         System.out.println("method B");  
  4.     }  
  5. }  
  6. public class InterfaceStaticMethod implements StaticMethod {  
  7.     public static void main(String[] args) {  
  8.         //access static method of Interface StaticMethod with its name    
  9.         StaticMethod.B();  
  10.     }  

This will produce an output as: method B
 
forEach() method in interface(iterable)
 
forEach() method was introduced in Java 8 to iterate collection of elements. This method is useful when we want to iterate over the collection only. We can pass lambda expressions as an argument. We can even use Method Reference in this as an argument.
 
Signature of forEach() : default void forEach(Consumer<super T> action)
 
Where, Consumer<super T> is an interface added in Java 8 and is a type of functional Interface where it has only one abstract method with generic parameters of any type.
 
Example
 
ExampleForEachDemo.java
  1. public class ExampleForEachDemo {  
  2.     public static void main(String[] args) {  
  3.         List < String > list = new ArrayList < > ();  
  4.         list.add("One");  
  5.         list.add("Two");  
  6.         list.add("Three");  
  7.         list.forEach(i - > System.out.println(i)); //Internal ForEach method  
  8.     }  

This code will provide output as,
 
One
Two
Three
 
Optional Class
 
Is Introduced in Java 8, and provides an easier way to avoid NullPointerException. This class has a basic feature to avoid program termination with NullPointerException if it occurs, to overcome this optional class comes into the picture.
 
By using optional instances we can specify alternate values to return or alternate code to run.
 
Different ways to use Optional Class,
  1. Get the value.
  2. Get if object is not null, else throw Exception
  3. Get if object is not null, else return default
  4. Consume the data
Example
 
OptionalClassDemo.java
  1. public class OptionalClassDemo {  
  2.     static String address = "Uttar Pradesh";  
  3.     public static void main(String[] args) {  
  4.         Optional < String > n = Optional.ofNullable(address);  
  5.         if (n.isPresent()) System.out.print(n.get());  
  6.         System.out.print("India");  
  7.     }  

This code will provide output as - Uttar Pradesh
 
Date Time API
 
The Java.time package contains Java 8 Date and Time classes. The classes represent principle date-time concepts, including instances, duration, date, time, time-zones and periods. All these classes are immutable and thread safe.
 
Some important classes included in Date Time API’s are,
  1. java.time.LocalTime
  2. java.time.LocalDate
  3. java.time.LocalDateTime
  4. java.time.Clock
Example
 
DateTimeAPIDemo.java
  1. public class DateTimeAPIDemo {  
  2.     public static void main(String[] args) {  
  3.         LocalDate d = LocalDate.now();  
  4.         System.out.println(d); //today's date  
  5.         System.out.println(d.plusDays(10)); //date after 10 days  
  6.     }  

This code output as,
 
2020-07-14
2020-07-24
 
Nashorn JavaScript Engine
 
Nashorn JavaScript Engine was introduced in Java 8 and is used to interpret Javascript code in Java applications or from the command line. It is an implementation of ECMAScript edition 5.1.
 
Example
  1. C:\Users\Dell>jjs  
  2. jjs> print("Hello World")  
  3. Hello World  
  4. jjs> a = 10  
  5. 10  
  6. jjs> b = 20  
  7. 20  
  8. jjs> a + b  
  9. 30  
  10.   
  11. jjs> quit() 

What have we learned ?

 
After reading this article, users have a brief idea what Java 8 includes and can implement it in their codes. For a more detailed explanation on any feature of Java 8 check my other articles.
 
Note
While trying to work with any feature of java 8 remember to use JDK 1.8 or higher else these concepts and code will not work.
 

Summary

 
Coming to the end of the post let us have a quick recap of what we have learned so far,
  • Introduction with Java 8
  • Features of Java 8 and their implementation.
  • Lambda Expressions
  • Stream API
  • Functional Interface
  • Functional Programming and more.