TABLE OF CONTENTS (HIDE)

Java 8

New Features

Java 8 is a major upgrade! It introduces new syntaxes (Lambda Expression) to support functional programming; retrofitted existing libraries (especially the Collection Framework); and added new libraries and features.

Functional Programming

Java ...

In order to support functional programming, Java 8 re-designs the interface, introduces lambda expression and retrofits the Collection framework. We shall describe these changes before presenting the functional programming in details.

Interfaces: Default and Static Methods

Prior to JDK 8, a Java interface can contain only abstract public methods and constants (public static final fields). As a consequence, designing and maintaining interfaces becomes difficult because if we were to add an additional method to the interface, all the implementation classes must be retrofitted. Hence, most frameworks provide a base implementation class, let you extend it and override the methods in your application.

To resolve the above constraint, starting from JDK 8, an interface can include static public methods and default public methods. (JDK 9 may include private method.) The default and static method are non-abstract. You need to provide the implementation (method body). These behaviors are inherited by the subclasses. Moreover, adding default or static method to an existing interface does not require retrofitting existing implementation classes.

Interface Default Methods

JDK 8 interface supports default methods via a new keyword "default". Default methods are non-abstract. You need to provide the method body. Furthermore, the implementation classes are not require to override the default methods. The default methods are public.

public interface MyJ8Interface {
   void foo();   // abstract public

   // Default methods are marked by keyword "default"
   default void bar() {    // public
      System.out.println("run default in MyJ8Interface");
   }
   
   //default void barX();
   // error: missing method body, or declare abstract
}

Notes: If you did not provide the method body to default method, you will receive compilation error "missing method body, or declare abstract".

public class MyClass implements MyJ8Interface {
   // Need to override all the abstract methods,
   // but not necessary to override the default methods.
   @Override
   public void foo() {
      System.out.println("run foo() in MyClass");
   }

   public static void main(String[] args) {
      MyClass c = new MyClass();
      c.foo();  // run foo() in MyClass
      c.bar();  // run default in MyJ8Interface
   }
}
Implementing Multiple Interfaces

Java classes can implement multiple interfaces (but extend only one superclass). In the above example, if another interface (say MyJ8Interface1) also provides a default method bar(), and a class (say MyClass1) implements both MyJ8Interface and MyJ8Interface1, a problem arises. To resolve this problem, JDK 8 requires the implementation classes to override the default methods if more than one versions are inherited. For example,

public interface MyJ8Interface1 {
   void foo1();   // abstract public

   // Same signature (but different implementation) as the default method in MyJ8Interface
   default void bar() {   // public
      System.out.println("run default in MyJ8Interface1");
   }
}
public class MyClass1 implements MyJ8Interface, MyJ8Interface1 {
   @Override
   public void foo() {
      System.out.println("run foo() in MyClass1");
   }
   @Override
   public void foo1() {
      System.out.println("run foo1() in MyClass1");
   }
   @Override
   public void bar() {
      System.out.println("Run default in MyClass1");
   }
   // bar() exists in both interfaces. Must override, or error:
   //   class MyClass1 inherits unrelated defaults for bar()
   //   from types MyJ8Interface and MyJ8Interface1

   public static void main(String[] args) {
      MyClass1 c = new MyClass1();
      c.foo();   // run foo() in MyClass1
      c.foo1();  // run foo1() in MyClass1
      c.bar();   // Run default in MyClass1
   }
}

Interface Static Methods

The static method is similar to the default method, but it cannot be overridden (but can be hidden) in the subclasses.

public interface MyJ8Interface2 {
   void foo2();   // abstract public

   static void bar2() {    // public
      System.out.println("run static in MyJ8Interface2");
   }

   //static void barX();
   // error: missing method body, or declare abstract   
}

Like default methods, static methods are non-abstract and you need to provide the method body. Otherwise, it triggers compilation error "missing method body, or declare abstract".

public class MyClass2 implements MyJ8Interface2 {
   @Override
   public void foo2() {
      System.out.println("run foo2() in MyClass2");
   }

   public static void main(String[] args) {
      MyClass2 c = new MyClass2();
      c.foo2();  // run foo2() in MyClass2
      MyJ8Interface2.bar2();  // run static in MyJ8Interface2
      // c.bar2();  // error: cannot find symbol: method bar2()
      // Interface static methods are not available to the instances!!!
   }
}
Are Java's static methods inherited in the subclasses?

Yes.

Can Java's static methods be overridden in the subclasses?

No, static methods cannot be "overridden" in the subclasses, but they can be "hidden" in the subclasses. The difference between "overridden" and "hidden" lies in polymorphism. During polymorphism, the version of the "overridden" method that gets invoked is the one in the subclass. The version of the "hidden" method that gets invoked depends on whether it is invoked from the superclass or the subclass.

For example,

class A {
   public static void foo() {
      System.out.println("foo() in class A");
   }
   public static void bar() {
      System.out.println("bar() in class A");
   }
}

class B extends A {
   //@Override  // error: method does not override or implement a method from a supertype
   // Not "override" but "hide" the superclass method.
   public static void foo() {
      System.out.println("foo() in class B");
   }
}

public class TestStaticMethodInheritance {
   public static void main(String[] args) {
      // Invoke static method from classes
      A.foo();   // foo() in class A
      B.foo();   // foo() in class B
      A.bar();   // bar() in class A
      B.bar();   // bar() in class B (static methods are "inherited")

      // Invoke static method From instances
      A a = new A();
      a.foo();   // foo() in class A
      a.bar();   // bar() in class A
      B b = new B();
      b.foo();   // foo() in class B
      b.bar();   // bar() in class A ("inherited")

      // Test Polymorphism
      A a1 = new B();
      a1.foo();  // foo() in class A (non-polymorphic)
                 // a1 is a reference of class A,
                 // hence, class A's version is invoked.
   }
}

Notes;

  1. Marking the subclass' static method with @Override triggers a compilation error.
  2. You cannot use a non-static method in the subclass to override or hide a superclass' static method.

Interface Instance Methods

There are 3 kinds of method in JDK-8 interfaces: abstract, default and static. All methods are public.

JDK 8 documentation for interfaces lists "Instance methods", which includes all non-static methods (abstract methods and default methods). It also lists "All methods", "Static methods", "Abstract methods" and "Default methods".

Interface vs. Abstract Superclass

  1. Variables: Interface can contain only constants (public static final variables).
  2. Method Access Control: All methods (abstract, static and default) in interfaces are public. JDK 9 supports private methods.

Lambda Expressions, Functional Interfaces and Collections

Reference:
  1. Java Tutorial "Lambda Expression" @ http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html.
  2. "Java SE 8: Lambda Quick Start" @ http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html.
  3. Java Tutorial "Collections" @ http://docs.oracle.com/javase/tutorial/collections/.
  4. Java Tutorial "Aggregate Operations" @ http://docs.oracle.com/javase/tutorial/collections/streams/index.html.

 

The most notable new feature in JDK 8 is the Lambda Expression, which provides a clear and concise notation to construct an instance implementing a "Single-Abstract-Method Interface". In JDK 8, a "Single-Abstract-Method Interface" (i.e., an interface containing only one abstract method) is known as a "Functional Interface".

JDK 8 also retrofitted the Collection framework, with Lambda expression, functional interfaces and the introduction of streams API, to support chaining and aggregate operations (or filter-map-reduce).

Functional Interfaces

JDK has many "Single-Abstract-Method Interfaces" (called "Functional Interface" in JDK 8). The most commonly-used are: java.awt.event.ActionListener (used as ActionEvent handler), java.lang.Runnable (for starting a thread) and java.util.Comparator (used in Collections.sort() or Arrays.sort()).

These interfaces are commonly used in anonymous inner classes to construct an anonymous instance.

@FunctionInterface Annotation

The @FunctionInterface annotation can be used to mark that an interface contains only one abstract method. This is useful to prevent accidental addition of extra abstract methods into a functional interface.

Example 1: Swing Listener Lambda

In Swing program, we use an ActionListener to handle the ActionEvent, triggered by pushing a button. ActionListener is a Functional interface containing a single abstract method, defined as follows:

package java.awt.event;
@FunctionalInterface
public interface ActionListener extends java.util.EventListener {
   void actionPerformed(ActionEvent e);  // public abstract
}

The following Swing Counter program contains a textfield to display the count, and 2 buttons for counting up and counting down.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class CounterLambda extends JFrame {
   JTextField tfCount;
   int count = 0;

   public CounterLambda() {
      Container cp = getContentPane();
      cp.setLayout(new FlowLayout());

      cp.add(new JLabel("Counter"));
      tfCount = new JTextField(count + "", 8);
      tfCount.setEditable(false);
      cp.add(tfCount);

      // Using an anonymous inner class as ActionEvent handler
      JButton btnUp = new JButton("Count Up");
      cp.add(btnUp);
      btnUp.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            tfCount.setText(++count + "");
         }
      });

      // Using a Lambda Expression to return an instance of ActionListener
      JButton btnDown = new JButton("Count Down");
      cp.add(btnDown);
      btnDown.addActionListener(e -> tfCount.setText(--count + ""));

      setSize(400, 100);
      setVisible(true);
   }
   public static void main(String[] args) {
      // Using Lambda Expression to return a Runnable instance
      SwingUtilities.invokeLater(() -> new CounterLambda());
   }
}
How It Works
  1. Using an anonymous inner class (Line 19 to 23) requires at least 5 lines of codes, which can be replaced by a one-liner Lambda Expression (Line 28). I will explain the syntax in the following section.
  2. Similar, the Runnable can be coded in a one-liner using Lambda Expression (Line 35).
JavaFX

For JavaFX, you can also replace an anonymous inner class EventHandler with a one-line lambda expression. For example,

// Anonymous inner class
btn.setOnAction(new EventHandler<ActionEvent>() {
   @Override
   public void handle(ActionEvent e) {
      System.out.println("Hello World!");
   }
});

// Lambda Expression
btn.setOnAction(e -> System.out.println("Hello World!"));

Syntax and Usage of Lambda Expressions

Prior to JDK 8, to construct an instance that implements a Functional Interface requires many lines of codes. Lambda Expression provides a shorthand notation. Moreover, you can pass the lambda expression as a method argument (treating code as data), as shown in the above example.

Lambda Expression defines the "sole method" of a Functional Interface. It consists of 2 parts: parameters and method body, separated by ->. The parameters are separated by commas and enclosed by parentheses. The parentheses can be omitted if there is only one parameter. The method body could be a statement or a block. The method name is omitted, as it is the sole abstract method of the Functional Interface. The parameters' type and the return type are also optional, as it can be inferred from the method.

For examples:

() -> statement    // No argument and one-statement method body

arg -> statement   // One argument (parentheses can be omitted) and method body 

(arg1, arg2, ...) -> { 
   body-block 
}   // Arguments separated by commas and block body

(Type1 arg1, Type2 arg2, ...) -> { 
   method-body-block;
   return return-value; 
}   // With arguments and block body

In other languages that support function variables or function objects (such as C++ and Python), Lambda is used to define an anonymous function. However, in JDK 8, Lambda expression seems to define the method implementation for an instance of a Single-Abstract-Method Interface?!

In fact, if you try to write:

int i = () -> 5;
// error: incompatible types: int is not a functional interface

But,

// Using Lambda expression to construct a Runnable instance.
// In other words, Lambda expression returns an instance of Function Interface
Runnable r1 = () -> System.out.println("run run()");
   // Runnable is a functional interface
   // Lambda expression is used to define the implementation of the abstract method run()

// Using Lambda expression to construct an ActionListener instance
ActionListener lis = e -> System.out.println("run actionPerformed()");

Java is an Object-oriented Programming language. Everything in Java are objects (except primitives for simplicity). Functions are not objects in Java (but part of an object), and hence, they cannot exist by themselves. Unlike other languages such as C++, Python and JavaScript (known as Functional Programming Language), functions can exist by themselves.

JDK 8's Functional Interface and Lambda Expression allow us to construct a "function object" in a one-liner (or a few lines of codes). However, it can be used only for objects with one method.

Example 2: Runnable Lambda

The Runnable interface contains a single abstract method, defined as follows:

@FunctionalInterface
public interface Runnable {
   void run();  // public abstract
}

We can create a Runnable object via anonymous inner class (Pre JDK 8) or Lambda Expression (JDK 8), as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestRunnableLambda {
   public static void main(String[] args) {
      // Using an anonymous inner class
      Runnable r1 = new Runnable() {
         public void run() {
            System.out.println("Runnable 1");
         }
      };
      // Using a one-liner Lambda Expression for One-Method Interface
      Runnable r2 = () -> System.out.println("Runnable 2");

      r1.run();
      r2.run();
   }
}

[TODO] Explanation

Example 3: Functional Interface

Let's define a Functional Interface to denote a binary operator (such as add and subtract), as follows:

@FunctionalInterface
public interface IntBinaryOperator {
   int apply(int left, int right);
}
In the following class, the method operate() takes 2 int's and an object of the above Functional Interface as parameters, and carries out the binary operation.
public class MathOperation {
   public int operate(int left, int right, IntBinaryOperator op) {
      return op.apply(left, right);
   }
   public IntBinaryOperator add = (a, b) -> a + b;
   public IntBinaryOperator sub = (a, b) -> a - b;
   public IntBinaryOperator mul = (a, b) -> a * b;
   public IntBinaryOperator div = (a, b) -> a / b;

   // Test Driver
   public static void main(String args[]){
      MathOperation op = new MathOperation();
      System.out.println("8 + 9 = " + op.operate(8, 9, op.add));
      System.out.println("8 - 9 = " + op.operate(8, 9, op.sub));
      System.out.println("8 x 9 = " + op.operate(8, 9, op.mul));
      System.out.println("8 / 9 = " + op.operate(8, 9, op.div));
      System.out.println("2 ^ 5 = " + op.operate(2, 5, (a, b) -> (int)Math.pow(a, b)));
   }
}

[TODO] Explanation

Notes: The JDK 8 new package java.util.function provides a standard Functional Interface called IntBinaryOperator, exactly the same as the above.

Example 4: Comparator Lambda

We can use the static method Collections.sort() to custom sort a Collection object, which has the following signature:

public static <T> void sort(List<T> list, Comparator<? super T> c)

The second argument of sort() is a Functional Interface Comparator, which contains an abstract method to compare two objects of the given Collection, defined as follows:

package java.util;
@FunctionalInterface
public interface Comparator<T> {
   int compare(T o1, T o2);  // public abstract
}

Suppose that we have a List of Person objects, and want to perform a custom sort. Again, we could use an anonymous inner class (Pre JDK 8), or a Lambda Expression (JDK 8) to construct a Comparator instance.

Person.java
public class Person {
   private String name;
   private int age;

   public Person(String name, int age) {  // Constructor
      this.name = name; this.age = age;
   }
   public String getName() {
      return name;
   }
   public int getAge() {
      return age;
   }
   public String toString() {
      return name + "(" + age + ")";
   }
   // To be used in testing Consumer
   public void sayHello() {
      System.out.println(name + " says hello");
   }
}
import java.util.*;
public class TestComparatorLambda {
   public static void main(String[] args) {
      List<Person> pList = new ArrayList<>();
      pList.add(new Person("Peter", 21));
      pList.add(new Person("Paul", 18));
      pList.add(new Person("Patrick", 22));
      System.out.println(Arrays.toString(pList.toArray()));
         // Unsorted: [Peter(21), Paul(18), Patrick(22)]

      // Using an anonymous inner class to create a Comparator instance
      Collections.sort(pList, new Comparator() {
         @Override
         public int compare(Person p1, Person p2){
            return p1.getName().compareTo(p2.getName());  // String's compareTo()
         }
      });
      System.out.println(Arrays.toString(pList.toArray()));
         // Sort by name: [Patrick(22), Paul(18), Peter(21)]

      // Using a Lambda Expression to create a Comparator instance
      Collections.sort(pList, (p1, p2) -> p1.getAge() - p2.getAge());
      System.out.println(Arrays.toString(pList.toArray()));
         // Sort by age: [Paul(18), Peter(21), Patrick(22)]
   }
}
How It Works
  1. For the first sort(), we use an anonymous inner class to construct an instance of Comparator; whereas for the second sort(), we replaced by a one-liner lambda expression to construct an instance of Comparator.

Example 5: Collection

JDK 8 did a major revamp to the Collection framework, by integrating with Lambda expression, and introducing Stream API to support chaining and aggregate operations (filter-map-reduce) on a Collection.

This example is modified from Java Tutorial.

Use Case - Filter and Reduce

Suppose that we have a List (a subclass of Collection) of Person objects (defined in Person.java in the above example) and we want to:

  1. Loop through the entire list,
  2. Filter with a certain criteria (e.g., age >= 21),
  3. Run certain operations to the filtered list (e.g., invoke sayHello(), toString(), or find the average age). This is known as a reduction operation which allows you to compute a result.

The codes should be general to handle any filtering criteria and run any reduction operations.

Approach 1: Roll Your Own

Person.java: As in the above example.

PersonPredicate.java: Define a Functional Interface called PersonPredicate to perform filtering, based on a boolean function test(), as follows:

@FunctionalInterface
public interface PersonPredicate {
   boolean test(Person p);  // Perform this boolean test on the given Person
}

PersonConsumer.java: Define a Functional Interface called PersonConsumer to run some operations on the Person object (coded inside accept()), as follows:

@FunctionalInterface
public interface PersonConsumer {
   void accept(Person p);  // Run these operations on the given Person
}

ProcessPersonList.java: Define a static method to carry out the filter-reduce operation, by looping through the List:

import java.util.List;
public class ProcessPersonList {
   // Given a List of Person, filter with predicate, and consume.
   public static void FilterReduce(List<Person> pList, PersonPredicate predicate, PersonConsumer consumer) {
      for (Person p:pList) {
         if (predicate.test(p)) {  // Filter
            consumer.accept(p);    // Reduce
         }
      }
   }
}

TestProcessPersonList.java: We can test with various filters and reduction operations as in the following test driver:

import java.util.*;
public class TestProcessPersonList {
   public static void main(String[] args) {
      // Create a List of Person objects
      List<Person> pList = new ArrayList<>();
      pList.add(new Person("Peter", 21));
      pList.add(new Person("Paul", 60));
      pList.add(new Person("Patrick", 15));
      System.out.println(Arrays.toString(pList.toArray()));
         // [Peter(21), Paul(60), Patrick(15)]

      // Pre JDK 8: Using anonymous inner classes
      ProcessPersonList.FilterReduce(
         pList,
         new PersonPredicate() {
            @Override
            public boolean test(Person p) {
               return p.getAge() >= 21;  // Filtering criteria
            }
         },
         new PersonConsumer() {
            @Override
            public void accept(Person p) {
               p.sayHello();   // Apply this operation
            }
         }
      );

      // JDK 8: Using Lambda Expressions
      ProcessPersonList.FilterReduce(pList, p -> p.getAge() >= 21, p -> p.sayHello());
   }
}

[TODO] Explanation

Approach 2: Using JDK 8 Pre-defined Functional Interfaces

JDK 8 added a new package java.util.function, which contains many standard Functional Interfaces, including Predicate and Consumer, defined with generic, as follows:

java.util.function.Predicate:

package java.util.function;
@FunctionalInterface
public interface Predicate<T> {
   public boolean test(T t);  // Evaluates this predicate on the given object.
}

java.util.function.Consumer:

package java.util.function;
@FunctionalInterface
public interface Consumer<T> {
   public void accept(T t);  // Run this operation on the given object.
}

Instead of defining our Functional Interfaces (the non-generic PersonPredicate and PersonConsumer) in the above, we shall use the standard ones.

ProcessPersonList.java: We add a method FilterReduce1(), which uses the standard functional interfaces Predicate<Person> and Consumer<Person>:

import java.util.List;
import java.util.function.Predicate;
import java.util.function.Consumer;
public class ProcessPersonList {
   // Create a List of Person objects
   ......

   public static void FilterReduce1(List<Person> pList, Predicate<Person> predicate, Consumer<Person> consumer) {
      for (Person p:pList) {
         if (predicate.test(p)) {
            consumer.accept(p);
         }
      }
   }
}

TestProcessPersonList.java: We can test this new method as follows:

import java.util.*;
public class TestProcessPersonList {
   public static void main(String[] args) {
      ......
      // Using JDK 8 standard functional interfaces Predicate<T> and Consumer<T>
      ProcessPersonList.FilterReduce1(pList, p -> p.getAge() >= 21, p -> p.sayHello());      
   }
}
Approach 3: Filter-Map-Reduce

Suppose that instead of operating on the filtered List (of Person objects), we want to operate on a certain property of Person (e.g., name), then we need to add a mapper or transformer (i.e., Filter-Map-Reduce pattern). We shall use the standard Functional Interface java.util.function.Function as our mapper, defined as follows:

java.util.function.Function:

package java.util.function;
@FunctionalInterface
public Function<T, R> {
   public R apply(T t);  // Apply this mapping to the given object.
}

ProcessPersonList.java: We add a new method FilterReduce2():

import java.util.List;
import java.util.function.Predicate;
import java.util.function.Consumer;
import java.util.function.Function;
public class ProcessPersonList {
   // Given a List, filter with predicate, apply mapper, and reduce (filter-map-reduce)
   public static void FilterReduce2(List<Person> pList, 
                                    Predicate<Person> predicate, 
                                    Function<Person, String> mapper, 
                                    Consumer<String> consumer) {
      for (Person p:pList) {
         if (predicate.test(p)) {
            String s = mapper.apply(p);  // Apply mapper to transfom Person to String
            consumer.accept(s);
         }
      }
   }
}

TestProcessPersonList.java: We can test this new method as follows:

import java.util.*;
public class TestProcessPersonList {
   public static void main(String[] args) {
      ProcessPersonList.FilterReduce2(
         pList, 
         p -> p.getAge() >= 21, 
         p -> p.getName(), 
         name -> System.out.println(name)
      );

      // Using method references
      ProcessPersonList.FilterReduce2(pList, p -> p.getAge() >= 21, Person::getName, System.out::println);
   }
}

[TODO] Explanation

Method References

JDK 8 introduced a new operator :: to reference a method without invoking it - called Method Reference. For example,

// Method References
System.out::println
Person::getName
Person::sayHello
"xyz"::length
// Constructor References
Integer::new
int[]::new

We can replace Lambda Expression p -> p.method() with a method reference ClassName::method, as in the above example.

Approach 4: Using JDK 8's Streams API and Pipeline

JDK 8 added a Streams API to the Collection framework to support aggregate operations. This can simplify the above filter-map-reduce to a one-liner. Furthermore, there is no need for an explicit for-loop.

For example,

import java.util.*;
import java.util.function.Predicate;
public class TestProcessPersonList {
   public static void main(String[] args) {
      ......
      
      // Using JDK 8 Stream for filter-reduce
      pList.stream().filter(p -> p.getAge() >= 21).forEach(p -> p.sayHello());
      pList.stream().filter(p -> p.getAge() >= 21).forEach(Person::sayHello);  // Using method reference

      // Using map() to extract a specific property from the object
      Predicate<Person> adult = p -> p.getAge() >= 21;
      pList.stream().filter(adult).map(p -> p.getName()).forEach(name -> System.out.println(name));
      pList.stream().filter(adult).map(Person::getName).forEach(System.out::println);

      // Apply aggregate operation average(), sum() to an int property extracted via mapToInt()
      System.out.println(pList.stream().filter(adult).mapToInt(p -> p.getAge()).average().getAsDouble());
      System.out.println(pList.stream().filter(adult).mapToInt(Person::getAge).average().getAsDouble());
      System.out.println(pList.stream().filter(adult).mapToInt(Person::getAge).sum());
   }
}
Pipeline

A pipeline is a sequence of operations on a Collection (or array). The sequence composes:

  1. A source: a collection or an array, e.g., pList (which is a List of Person objects) in the above examples.
  2. stream(): produce a Stream, which is a sequence of elements to be carried from the source into the pipeline.
  3. Some intermediate operations: for example, filter(Predicate), which creates a new Stream consisting of elements that matches the Predicate.
  4. A terminal operation (the reduction operation): such as forEach(), which produces the desired result.
The java.util.stream.Stream Interface

A Stream is a sequence of elements supporting sequential and parallel aggregate operations in a stream pipeline. We can create a sequential Stream via the default method in the Collection interface stream(); or create a parallel Stream via method parallelStream(), defined as follows:

interface Collection<E> {
   default Stream<E> stream()          // Returns a sequential Stream with this Collection as its source
   default Stream<E> parallelStream()  // Returns a possibly parallel Stream with this Collection as its source
   ......
}

There are 3 primitive type specializations for Stream: IntStream, LongStream and DoubleStream.

[TODO]

The methods in the pipeline chain
  • aCollection.stream(): returns a sequential Stream with this Collection as its source.
  • aStream.filter(aPredicate): Filter this Steam with the given java.util.function.Predicate object.
    Stream<T> filter(Predicate<? super T> predicate)
  • aStream.forEach(aConsumer): Run the operation in the java.util.function.Consumer object.
    void forEach(Consumer<? super T> action)
  • aStream.map(aFunction): Apply the mapping function (transformation) in the given java.util.function.Function object to map from T to R.
    <R> Stream<R> map(Function<? super T, ? extends R> mapper)
  • aStream.mapToInt(anToIntFunction): A type specialization of map() that returns an IntStream (a primitive type specialization of Stream).
    IntStream mapToInt(ToIntFunction<? super T> mapper)
    
  • anIntStream.average().getAsDouble(): Compute the average for this IntStream.
    OptionalDouble average()  // Returns an OptionalDouble for the average of this stream,
                              // or an empty OptionalDouble if this stream is empty.
    The result is a java.util.OptionDouble object, which is a container may or may not contain a double value. If a value is present, isPresent() will return true and getAsDouble() will return the value.
  • anIntStream.sum(): Compute the sum of this IntStream and return the int sum.
    int sum()  // Returns the sum of elements in this stream as int

JDK 8 java.util.function package

JDK 8 provides a new package java.util.function, which provides a number of standard Functional Interfaces, such as:

Predicate: A property of the object passed as argument, T -> boolean

package java.util.function;
@FunctionalInterface
public interface Predicate<T> {
   public boolean test(T t);  // Evaluates this predicate on the given argument.
}

You can use Predicate to evaluate a condition for a Collection. Since Predicate is a Functional Interface, you can pass a lambda expression wherever the Predicate is expected.

Consumer: An action to be performed with the object passed as argument,

package java.util.function;
@FunctionalInterface
public interface Consumer<T> {
   public void accept(T t);  // Performs this operation on the given argument.
}

Function: Transform a T to a R, T -> R

package java.util.function;
@FunctionalInterface
public Function<T, R> {
   public R apply(T t);  // Applies this function to the given argument.
}

Supplier: Provide an instance of a T (such as a factory)

package java.util.function;
@FunctionalInterface
public Supplier<T> {
   public T get();  // Gets a result.
}

UnaryOperator: A unary operator, T -> T

package java.util.function;
@FunctionalInterface
public UnaryOperator<T> {
   static <T> UnaryOperator<T> identity()  // Returns a unary operator that always returns its input argument.
}

BinaryOperator: A binary operator, (T, T) -> T

package java.util.function;
@FunctionalInterface
public BinartOperator<T> {
   static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator);
   static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator);
}

IntBinaryOperator:

package java.util.function;
@FunctionalInterface
public IntBinartOperator<T> {
   int applyAsInt(int left, int right);
}
Primitive Type Specializations

Many of the above Functional Interfaces have type specialization for primitives. For examples,

  • IntBinaryOperator:
    package java.util.function;
    @FunctionalInterface
    public IntBinartOperator<T> {
       int applyAsInt(int left, int right);
    }
  • IntFunction:
    package java.util.function;
    @FunctionalInterface
    public IntFunction<R> {
       R apply(int value);
    }
  • IntToDoubleFunction:
    package java.util.function;
    @FunctionalInterface
    public IntToDoubleFunction {
       double applyAsDouble(int value);
    }
  • Many more.

JDK 8 java.util.stream package

[TODO]

Iterable's forEach()

JDK 8 introduces a new method forEach() to Iterable (super-interface of Collection), which is available to all Collection objects.

This forEach() is a default method in the Iterable interface, defined as follows:

default void forEach(Consumer<? super T> action) {
   for (T t : this) {
      action.accept(t);
   }
}
Example

Person.java: as above.

TestIterableForEach.java:

import java.util.*;
public class TestIterableForEach {
   public static void main(String[] args) {
      List<Person> pList = new ArrayList<>();
      pList.add(new Person("Peter", 21));
      pList.add(new Person("Paul", 60));
      pList.add(new Person("Patrick", 15));
      System.out.println(Arrays.toString(pList.toArray()));

      // Pre JDK 8: Using for-loop
      for (Person p: pList) {
         System.out.println(p);
         System.out.println(p.getName());
      }

      // Using Lambda Expression
      pList.forEach(p -> System.out.println(p));
      pList.forEach(p -> System.out.println(p.getName()));
      pList.forEach(p -> p.sayHello());

      // Using Method Reference
      pList.forEach(System.out::println);
      pList.forEach(Person::sayHello);
} }

[TODO] Explanation

Notes: The Iterable.forEach() is different from Stream.forEach().

JDK 8 Date Time API

References: The Java Tutorial: Date Time @ https://docs.oracle.com/javase/tutorial/datetime/.

JDK 8 revamped the date-time support by introducing a new package java.time to provide a comprehensive support of dates, times, instants, durations, timezones, and periods. This new date-time API is based on ISO-8601 (Gregorian calendar system) as the default calendar.

The new date-time API consists of main package java.time and four subpackages:

  • java.time: Core API classes for date, time, date and time, time zones, instants, duration, and clocks. These classes are based on ISO-8601 (Gregorian calendar system). They are immutable and thread-safe.
  • java.time.format: Classes for formatting and parsing dates and times.
  • java.time.zone: Classes for time zones, time-zone offsets, and time zone rules (ZonedDateTime, and ZoneId or ZoneOffset).
  • java.time.temporal: Extended API for interoperations between the date and time classes, querying, and adjustment.
  • java.time.chrono: for calendar systems other than the default ISO-8601 (e.g., Thai Buddhist and Japanese), not commonly-used.

The API's methods have standardized perfixes as follows:

  • of: static factory methods for creating an instance.
  • parse: static factory methods for parsing the input string to produce an instance.
  • format: using the formatter to produce a string.
  • get: get an attribute.
  • with: returns a copy of the target object with one element changed. Similar to set, but all date-time classes are immutable.
  • is: query the state.
  • plus, minus: returns a copy with the amount of time added or subtracted.
  • to: convert to another type.
  • at: combine with another.

There are two ways to represent date-time:

  1. Human Time: in terms of year, month, day, hour, minute and second.
  2. Machine Time: in nanoseconds (or days) from an origin called epoch.

Thre are two main groups of classes:

  1. LocalXxx: without timezone, e.g., LocalDateTime, LocalDate, LocalTime.
  2. ZoneXxx: with timezone, e.g., ZoneDateTime, ZoneId, ZoneOffset.

Enum DayOfWeek and Month

Enum DayOfWeek

For representing day of the week, with 7 constants MONDAY (1) through SUNDAY (7) and various operations.

Enum Month

For representing month names, with 12 constants JANUARY (1) through DECEMBER (12) and various operations.

Local Date-Time classes

Local classes such as LocalDate, LocalTime, LocalDateTime represent a human date/time, without timezone.

Class LocalDate

For representing a date (year-month-day, without time and timezone). It cannot represent an instant on the timeline without additional information such as an offset or timezone.

To construct a LocalDate, use static method of(), parse() or now():

public static LocalDate of(int year, int month, int day)    // month:1-12, day:1-31
public static LocalDate of(int year, Month month, int day)  // Using "Month" enum
public static LocalDate ofYearDay(int year, int dayOfYear)  // dayOfYear:1-366

public static LocalDate parse(CharSequence text)  
      // In DateTimeFormatter.ISO_LOCAL_DATE e.g., "2017-12-24"
public static LocalDate parse(CharSequence text, DateTimeFormatter formatter)  
      // In the given DateTimeFormatter

public static LocalDate now()  // from system clock in the default timezone
public static LocalDate now(ZoneId)  // from system clock in the specified timezone
Class LocalTime

Representing a time in hour, minute and second; without timezone or day light saving.

Class LocalDateTime

Representing a date-time with year, month, day, hour, minute and second; without timezone.

Class Year
Class YearMonth
Class MonthDay

DateTime with TimeZone

Class ZonedDateTime
Class ZoneId
Class ZoneOffset
Class Clock

Machine Time

Class Instant
Class Duration
Class Period

Offset DateTime

Class OffsetDateTime
Class OffsetTime

Concurrency API Improvement

[TODO]

IO Improvement

[TODO]

Miscellaneous Core API Changes

min(), max(), sum() in Integer, Long and Double Wrapper Classes

[TODO]

Unsigned Integer

[TODO]

logicalAnd(), logicalOr(), logicalXor() in Boolean Wrapper Classes

[TODO]

REFERENCES & RESOURCES