Java 8 Language Features in Nutshell - part 2

Hi Friends,

This is a part-2 of Java 8 Language Features in nutshell series. You can go through part-1 by clicking following link Java 8 Language Features in Nutshell - Part 1 Here we will discuss following features of Java 8:

1. Static methods in Interface

2. Streams




1. Static methods in Interface

What is Static Method?

Static methods are the methods which belong to the class rather than to object or instance. Which in other words means that all instances of the class share same behavior defined in the static method. So you don't need to create an instance or object of a Class to invoke a static method.

So if static methods do not need an object to be invoked, it does not make much sense to put them in a class, because a class is a blueprint for all the objects which belongs to that class. So what if we could put all the static methods in an interface rather than in class. Good news is, Java 8 allows that. With Java 8, we can put our utility methods which we used to put in a class, now in an interface.

The syntax for Static Method in Interface

The syntax is just like any other static method.

static <Return Type> <Method Name>();

Main Purpose of a static method in Interface

The main reason to allow the addition of static methods in the interface is to have utility methods in the interface rather than in the class. Before Java 8, if we were to define some utility methods, we were creating a class and defining utility methods in that class and because we knew that it does not make sense to create object of a class having only utility methods, we were making constructor of that class as Private and marking the class as final, so that it could not be inherited, because we know that static methods are not inherited,so better mark it explicitly, so that just by looking at the class, one can know that this class could not be inherited.

Example of Static Method in  Interface

Before Java 8

import java.util.Date;

final class CustomerUtil {
    private CustomerUtil(){

    }
    public static Date convertDateFormat(){

    }
    public static String appendData() {

    }
}
class Test {
 public static void main(String[] args){
  CustomerUtil.convertDateFormat();  
 }
}


After Java 8

interface CustomerUtil {
   public static Date convertDateFormat(){

   }
   public static String appendData() {

   }
}
class Test {
  public static void main(String[] args){
    CustomerUtil.convertDateFormat();  
 }
}


2. Streams

What is Stream in Java 8

Stream is a sequence of elements from a source which supports aggregate operations. So every Stream will have one Source and one destination. Data flows from source to destination via Stream.

Following excerpt from Oracle Documentation describes very well ,the difference between Collection and Stream.


  • No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
  • Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
  • Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
  • Possibly unbounded. While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
  • Consumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source

The syntax for Stream

Stream can be created in multiple ways. We will see one of the ways which are used most frequently and will discuss rest in another post.



As we can see from above diagram that a new default method stream() has been added in Collection interface and as  List, Set, Queue interfaces extends Collection interface,so all these interfaces, in turn, has stream method, which can be used to create Stream from these collections as source.

Syntax to create Stream of Strings with List as a source

List<String> list = new ArrayList<>();
list.add("AA");

Stream<String> stream = list.stream();

Syntax to create Stream of Strings with Set as a source

Set<String> set = new HashSet<>();
set.add("AA");

Stream<String> stream = set.stream();

The main purpose of Stream

The main purpose to introduce  Stream API in Java is to have less verbose code to perform operations on a group of data like Collections, Array and to make use of multi-core processors(using parallel stream) without the programmer needing to write a single line of multithreading code, hence improvement in performance.

Example of a Stream Operation

Say you have a list of elements(Strings) and want to have a distinct list of elements. Following we will see how you will do it before Java 8 and with Java 8.

Before Java 8

List<String> list = new ArrayList<>();
list.add("AA");
list.add("BB");
list.add("BB");
list.add("BB");
System.out.println("Size of list before applying distinct logic:"+list.size());
List<String> distinctList1 = new ArrayList<String>();
for(String str : list){
  if(!distinctList1.contains(str)){
    distinctList1.add(str);
  }
}
System.out.println("Distinct List Size:"+ distinctList1.size());

After Java 8

List<String> sourceList = new ArrayList<>();
sourceList.add("AA");
sourceList.add("BB");
sourceList.add("BB");
sourceList.add("BB");
System.out.println("Size of list before applying Stream Operations:"+sourceList.size());
List<String> distinctList =  sourceList.stream().distinct().collect(Collectors.toList());
System.out.println("Distinct List Size:"+ distinctList.size());
In the above code

sourceList is a source of elements from Stream
distinct() and collect() are the stream operations.
distinctList is the destinition List.

As we can see from above example that with Java 8, code to operate on Collection is more like query rather than a lot of boilerplate code. Just like in SQL, we need not write logic to find maximum from a collection of elements.Sql provides max() function for that. Similarly, Java 8 streams provide lots of aggregate functions which we can chain as well to do multiple things in a single line. The advantage of this approach is that this code is less verbose and it makes use of multicore processors as well, hence improvement in performance as well.