jdk8_features.html

General Documentation

http://docs.oracle.com/javase/8/

API Docs

http://docs.oracle.com/javase/8/docs/api/index.html

Lamda Expressions

http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach7

Default Methods

http://zeroturnaround.com/rebellabs/java-8-explained-default-methods/

Source Code for the Lambdas Book

https://github.com/RichardWarburton/java-8-lambdas-exercises/tree/master/src/main/java/com/insightfullogic/java8/examples/chapter1

You Tube Videos

Jump Starting Lambda Programming

https://www.youtube.com/watch?v=bzO5GSujdqI

Brian Goetz

https://www.youtube.com/watch?v=MLksirK9nnE

Introduction

Anonymous classes can be used to specify an implementation of an interface. However when interfaces are very simple and contain just one method then lambda expressions are more concise and clear. A lambda expression allows us to focus on the method being implemented rather than the class. Java API has existing interfaces that contain just one method like ActionListener and Runnable and often there is a need to specify a function/method behavior without going through inheritance or declaring a class. Lambda expressions make this possible.

Example:

( Refer to source file lamdas1.java )

Suppose we have an interface and we need to use the method by creating an object and we are

only going to use the object in a very specific narrow scope .

interface i1

{

void foo() ;

}

To use this interface we can declare a class:

class classImplementsI1 implements i1

{

public void foo()

{

System.out.println( "Inside method foo." ) ;

}

}

and use it as:

i1 i1Object = new classImplementsI1() ;

i1Object.foo() ;

If we are only going to use the implementation of the "foo()" method in the lines above then creating the class is unnecessary. An anonymous class can be used instead.

i1 i1Object = new i1()

{

public void foo()

{

System.out.println( "Inside method foo." ) ;

}

} ;

i1Object.foo() ;

However Lambda expression goes one step further:

i1 i1Object = () -> System.out.println( "Inside method foo." ) ;

i1Object.foo() ;

We only needed to write the body for a method and that is captured by the Lambda expression. The task of determining the types and the class is left to the compiler. In this case the compiler looks at the left hand side first and determines that there is an interface with a single method.If there wasn't then the compiler would complain of an error. The "()" means that there are no input parameters to the function and the "->" means that the body follows next. In this case the body does not have any return values but if it did then the compiler would try to determine from the expressions what the return value is.

WildCards

If we have a variable of some type then we can assign that to a variable of a base type of the first type. That's the premise of inheritance.

Object obj1 = new String("Test" ) ;

However when we are dealing with Generic types we cannot follow the same approach. Here is the first attempt at doing so:

List<Object> listOfIntegers = new Vector<Integer>() ;

Conceptually if we were allowed to do the above then we can insert any object even say a "String" into a list of objects. However the underlying container contains "Integers" and not "Strings" so this would not be appropriate. Java is very string with generic types.

The following program is "wild1.java" .

import java.util.* ;

public class wild1

{

public static void main( String args[] )

{

List<Integer> listOfIntegers = new Vector<Integer>() ;

List<Object> generalList = listOfIntegers ;

//Above does not compile because

// now I have a list of Objects. I am free to add a String to the

// list of objects because

// a String is an Object. However the original type was

// a list of Integers and now am mixing

//Strings with Integers. Does not make sense.

Object obj1 = new String("Test" ) ;

} // public static void main( String args[] )

}

Let us examine another program called "wild2.java" .

import java.util.* ;

public class wild2

{

public static void main( String args[] )

{

List<Integer> listOfIntegers = new Vector<Integer>() ;

List generalList = listOfIntegers ;

List<?> wildcardList = listOfIntegers ;

//listOfIntegers = wildcardList ; //does not compile

listOfIntegers = generalList ; //warning only

listOfIntegers.add( new Integer(10) ) ; //fine

generalList.add( new Integer(10) );

// wildcardList.add( new Integer(10) );

// wildcardList.add( new Object() ) ;

//Every object in Java is an Object but since it may not be an Integer

// this code will not compile.

//wildcard is very strict. It does not know a type so

// will not let you add anything even an Object.

//Can we get something out ?

Object obj1 = wildcardList.get(0) ;

} // public static void main( String args[] )

}

As we can see we are allowed to do something like this:

List generalList = listOfIntegers ;

The "generalList" is the old style list. In the old style list the object being contained is of type "Object" and doesn't have any

type associated with it. We can also do something like:

List<?> wildcardList = listOfIntegers ;

What the wildcard notation represented by "?" is stating that the type is unknown and actually will never be known. So if

we try to do:

listOfIntegers = wildcardList ; //does not compile

We receive a compiler error. That is because we are trying to assign to a List that has a specific type associated with it. That type is "Integer". Once we have a List with an unknown type we cannot assign it to something with a type. However we can assign the

old style List to the "listOfIntegers" .

listOfIntegers = generalList ; //warning only

In the below code we can of course add an Integer object to a "listOfIntegers" but we cannot add anything to the

"wildcardList" because the this list's type is unknown.

In addition to the wildcard question mark ( known as an unbounded wildcard) we have bounded wild cards also.

The below example shows how we can use bounded wildcards.

import java.util.* ;

class fruit

{}

class banana extends fruit

{}

class orange extends fruit

{}

public class extend1

{

public void function1( Vector<fruit> someVector )

{

//notice this compiles

someVector.add( new orange() ) ;

someVector.add( new banana() ) ;

}

/*

I have a Vector that contains some derived class of fruit.

*/

public void function2( Vector<? extends fruit> someVector )

{

//I cannot add a derived class but can get something out

someVector.add(new banana() ) ;

// someVector.add(new orange() ) ;

//someVector.add(new fruit() ) ;

fruit fruitObj = someVector.get(0) ;

}

public void function3( Vector<? super orange> someVector )

{

//I can add something

someVector.add(new orange() ) ;

//but I cannot add the base classes

someVector.add(new fruit() ) ;

fruit fruitObj = someVector.get(0) ;

}

public static void main(String args[] )

{

extend1 extend1Obj = new extend1() ;

Vector<fruit> someVectorOfFruits = new Vector<fruit>() ;

extend1Obj.function1(someVectorOfFruits) ; //compiles ok

Vector<orange> orange1 = new Vector<orange>() ;

//extend1Obj.function1(orange1) ; // does not compiles

Vector<orange> someVectorOfOranges = new Vector<orange>() ;

//extend1Obj.function1(someVectorOfOranges) ; //does not compile

extend1Obj.function2(someVectorOfOranges) ; // compiles ok

extend1Obj.function3(someVectorOfFruits) ; // compiles ok

}

}

In "function1" the argument is a Vector of fruits. So we can add other objects like "oranges" and "bananas" because they

are also fruits.

We cannot do:

Vector<orange> orange1 = new Vector<orange>() ;

//extend1Obj.function1(orange1) ; // does not compiles

As we saw before we cannot convert a Vector of oranges to a Vector of Fruit. We can only pass in a Vector of fruits. To make

this a little bit more flexible we have bounded wildcards.

In this function:

public void function2( Vector<? extends fruit> someVector )

{

//I cannot add a derived class but can get something out

someVector.add(new banana() ) ;

// someVector.add(new orange() ) ;

//someVector.add(new fruit() ) ;

fruit fruitObj = someVector.get(0) ;

}

We can pass in a Vector of any type that is derived from fruit. Notice inside the function I cannot add anything. Let's say I was

adding a banana object to a Vector whose type is derived from something. Well that type may very well be a Vector of oranges

in which case adding a banana does not make sense. We cannot add a fruit because we it's possible that fruit could be a

banana also. So this vector cannot consume anything but we can get something out of it and we can be sure that what we get

out is a fruit because a Vector of a type that is passed to this function will be derived from the "fruit" type.

Similarly we can use lower bounded wildcards. Let's study the "function3" from the example above:

public void function3( Vector<? super orange> someVector )

{

//I can add something

someVector.add(new orange() ) ;

//but I cannot add the base classes

someVector.add(new fruit() ) ;

fruit fruitObj = someVector.get(0) ;

}

In this function we can add an orange because we know that a type passed will be a base class of orange. We

cannot add a fruit even though fruit is a base class of orange because there is a possibility that we could have another

base class of orange say citrus that is passed as a type. And it's possible that citrus may not be a fruit. Similarly I cannot

get an object out and assume that it is a fruit for the same reason.

Lamda Expressions

Syntax

The example in the introduction showed a simple lambda expression with the method having no parameters and a single statement. To insert multiple statements curly braces are used as illustrated in the following example:

i1 i1Object1 = () -> {

System.out.println( "Inside method foo. Step 1" ) ;

System.out.println( "Inside method foo. Step 2" ) ;

};

A lambda expression taking a parameter( lamdas3.java) :

interface i2

{

void foo(int arg1) ;

}

i2 i2Object = (arg1) -> System.out.println( "Inside method foo. Arg: " + arg1) ;

Notice that the type of argument was not specified in the parameter for the lamda expression. The compiler figures this part out also. If the argument is used in such a manner that is not supported by the type then a compiler error comes up.

Ex:

i2 i2Object = (arg1) -> System.out.println( "Inside method foo. Arg: " + ( (arg1+"")-1 ) ) ;

Compiler Output:

lamdas3.java:58: error: bad operand types for binary operator '-'

i2 i2Object = (arg1) -> System.out.println( "Inside method foo. Arg: "

+ ( (arg1+"")-1 ) ) ;

It is also possible to give explicit arguments:

interface i2

{

void foo(int arg1) ;

}

//Explicit Argument

i2 i2Object = (int arg1) -> System.out.println( "Inside method foo. Arg: " + arg1) ;

i2Object.foo( 4 ) ;

A lambda expression can also return arguments that match the signature of the method in the interface. Ex (lamdas3.java)

Ex:

interface i3

{

boolean foo(int arg1) ;

}

i3 i3Object = (arg1) -> true ;

It is also possible to explicitly use return statements.

i3Object = (arg1) ->{

return true ;

};

Scope

Lambda expressions variables do not have their own scope. What does this mean ? If a variable is defined inside a lambda expression then the scope is shared with the statements outside the lambda expression in the block. Example scope1.java

//----------------------------------------------------------------------------------------------------------

interface i1

{

void foo() ;

}

//----------------------------------------------------------------------------------------------------------

public class scope1

{

public void method1()

{

int x1 ;

i1 i1Object = () ->

{

int x1;

System.out.println("Inside method foo. ");

};

}

//----------------------------------------------------------------------------------------------------------

public static void main( String args[] )

{

System.out.println( "Inside main" ) ;

scope1 scopeObject = new scope1() ;

scopeObject.method1() ;

}

//----------------------------------------------------------------------------------------------------------

}

//----------------------------------------------------------------------------------------------------------

Compiling the above program gives the error :

scope1.java:22: error: variable x1 is already defined in method method1()

int x1;

What about using the variable x1 ? The same rules that apply to inner classes and anonymous classes apply here. However in JDK 8 the rule has been changed in that the variable that can be accessed has to be "effectively" final. (Scope2.java) . A variable like x1 that is used in the lambda expression can be initialized with a value but if the value of x1 is changed afterwards then there is a compiler error.

public class scope2

{

public void method1()

{

int x1 = 65 ;

//Causes a compiler error

//x1++ ;

i1 i1Object = () ->

{

System.out.println("Inside method foo. ");

System.out.println( "x1=" + x1 ) ;

};

i1Object.foo() ;

}

//----------------------------------------------------------------------------------------------------------

public static void main( String args[] )

{

System.out.println( "Inside main" ) ;

scope1 scopeObject = new scope1() ;

scopeObject.method1() ;

}

//----------------------------------------------------------------------------------------------------------

}

Intersection of Types

http://http://stackoverflow.com/questions/24693369/compilation-error-lambda-target-type-intersection-type

http://stackoverflow.com/questions/22807912/how-to-serialize-a-lambda

A lambda expression can implement multiple interfaces. As long as there are no conflicts the lambda expression can specify the types it is interested in. A conflict can occur when the interfaces have methods with different signatures.

import java.io.* ;

interface i1

{

public void foo() ;

}

interface i2

{

public void foo() ;

}

interface i3

{

public void foo() ;

}

interface i4

{

public int foo(int arg1) ;

}

interface i5

{

}

public class multiple1

{

public static void main( String args[] )

{

//Normal Lambda object

Runnable runnableObject = () -> System.out.println("Serializable!");

//Allowed

Runnable runnableObject1 = (Runnable & Serializable)() -> System.out.println("Serializable!");

if( runnableObject1 instanceof Serializable )

System.out.println( "runnableObject1 is Serializable") ;

//Allowed

i1 i1Object = (i1 & i2 )() -> System.out.println("2 interfaces with a single method of the same signature.");

if( i1Object instanceof i2 )

System.out.println( "i1Object also implements i2.") ;

i1Object.foo() ;

//Allowed

i1 i1Object1 = (i1 & i2 & i3)() -> System.out.println("3 interfaces with same signatures.");

//Compiler error

//i1 i1Object2 = (i1 & i2 & i4)() -> System.out.println("3 interfaces with different signatures.");

//Allowed

i1 i1Object3 = (i1 & i2 & i5)() -> System.out.println("3 interfaces with a marker interface.");

}

}

Functional Interfaces

A new package "java.util.function" has been introduced with JDK 8 . This basically has interfaces with a single function so that the programmer does not have to define their own interfaces in most cases.

Assume there is a Person class and we want to print out all the Persons in a list that satisfy a particular condition. We can create our own interface as in the below example.( File: func1.java )

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------------------

/*

Illustrating the functional interfaces

*/

class Person

{

public enum Sex {

MALE, FEMALE

}

String name;

//LocalDate birthday;

Sex gender;

String emailAddress;

int age ;

public int getAge()

{

return age ;

}

public Sex getGender()

{

return gender ;

}

public void printPerson()

{

System.out.println( "Printing Person:" + "Name = " + name ) ;

}

}

//----------------------------------------------------------------------------------------------------------

interface CheckPerson

{

boolean test(Person p);

}

//----------------------------------------------------------------------------------------------------------

public class func1

{

//----------------------------------------------------------------------------------------------------------

public static void printPersons(List<Person> roster, CheckPerson tester)

{

for (Person p : roster)

{

if (tester.test(p))

{

p.printPerson();

}

} //for

}

//----------------------------------------------------------------------------------------------------------

public static void main( String args[] )

{

System.out.println( "Inside main" ) ;

List<Person> roster1 = new Vector<Person>() ;

Person p1= new Person() ;

p1.name = "Ajay" ; p1.age = 20 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

p1= new Person() ;

p1.name = "Timothy" ; p1.age = 24 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

// roster1.forEach(System.out::println);

printPersons( roster1, (personObj) -> personObj.getAge()> 20 ) ;

}

//----------------------------------------------------------------------------------------------------------

}

//----------------------------------------------------------------------------------------------------------

In the example above "printPersons" is a method that takes a list and also an object that implements the interface "CheckPerson". This interface takes a single method that takes an argument of type "Person" and returns a boolean. There is no need to define our own interface. We can use one from the package "java.util.function" . The following example shows how this can be done.

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------------------

/*

Illustrating the functional interfaces

*/

class Person

{

public enum Sex {

MALE, FEMALE

}

String name;

//LocalDate birthday;

Sex gender;

String emailAddress;

int age ;

public int getAge()

{

return age ;

}

public Sex getGender()

{

return gender ;

}

public void printPerson()

{

System.out.println( "Printing Person:" + "Name = " + name ) ;

}

}

//----------------------------------------------------------------------------------------------------------

public class func1

{

//----------------------------------------------------------------------------------------------------------

public static void printPersons(List<Person> roster, Predicate<Person> tester)

{

for (Person p : roster)

{

if (tester.test(p))

{

p.printPerson();

}

} //for

}

//----------------------------------------------------------------------------------------------------------

public static void main( String args[] )

{

System.out.println( "Inside main" ) ;

List<Person> roster1 = new Vector<Person>() ;

Person p1= new Person() ;

p1.name = "Ajay" ; p1.age = 20 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

p1= new Person() ;

p1.name = "Timothy" ; p1.age = 24 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

// roster1.forEach(System.out::println);

printPersons( roster1, (personObj) -> personObj.getAge()> 20 ) ;

}

//----------------------------------------------------------------------------------------------------------

}

//----------------------------------------------------------------------------------------------------------

Output:

Inside main

Printing Person:Name = Timothy

The interface "Predicate" is defined to use generics.

Interface Predicate<T>

Type Parameters:

T - the type of the input to the predicate

boolean test(T t)

Evaluates this predicate on the given argument.

Writing Predicate<Person> means the method is defined as "boolean test(Person t)" . It is not important what the name of the method is; the only thing that matters the method takes a Person parameter and returns a boolean. The interface Predicate has other methods also. But a lambda expression can only have an interface with a single method so how is this possible. This is where default methods come in ( discussed in this tutorial ) . One of the default methods in class Predicate is "negate" . This method also returns a Predicate but represents a "logical negation". Let's see how this can be used in our example. Suppose instead of a person age greater than 20 we want persons whose age is less than 20 . This can be done as follows:

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------------------

/*

Illustrating the functional interfaces

*/

class Person

{

public enum Sex {

MALE, FEMALE

}

String name;

//LocalDate birthday;

Sex gender;

String emailAddress;

int age ;

public int getAge()

{

return age ;

}

public Sex getGender()

{

return gender ;

}

public void printPerson()

{

System.out.println( "Printing Person:" + "Name = " + name ) ;

}

}

//----------------------------------------------------------------------------------------------------------

interface CheckPerson

{

boolean test(Person p);

}

//----------------------------------------------------------------------------------------------------------

public class func2

{

//----------------------------------------------------------------------------------------------------------

public static void printPersons(List<Person> roster, Predicate<Person> tester)

{

tester = tester.negate() ;

for (Person p : roster)

{

if (tester.test(p))

{

p.printPerson();

}

} //for

}

//----------------------------------------------------------------------------------------------------------

public static void main( String args[] )

{

System.out.println( "Inside main" ) ;

List<Person> roster1 = new Vector<Person>() ;

Person p1= new Person() ;

p1.name = "Ajay" ; p1.age = 20 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

p1= new Person() ;

p1.name = "Timothy" ; p1.age = 24 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

// roster1.forEach(System.out::println);

printPersons( roster1, (personObj) -> personObj.getAge()> 20 ) ;

}

//----------------------------------------------------------------------------------------------------------

}

//----------------------------------------------------------------------------------------------------------

Output:

Inside main

Printing Person:Name = Ajay

By using the statement "tester = tester.negate() ;" we are filtering by the negation of the filter condition that is in the lambda expression. Now all the persons whose age are less than or equal to 20 are selected. Let us see how negate in the interface Predicate is implemented.

/**

* Returns a predicate that represents the logical negation of this

* predicate.

*

* @return a predicate that represents the logical negation of this

* predicate

*/

default Predicate<T> negate() {

return (t) -> !test(t);

}

Interestingly this uses a Lambda expression . So an object of Predicate is created using the expression of "(personObj) -> personObj.getAge()> 20". And the function "negate" uses the function "test" of the first Predicate object to construct another Predicate object. Similarly the function "or" of the Predicate interface is defined as:

default Predicate<T> or(Predicate<? super T> other)

This declaration uses the word "super" to make the Predicate less restrictive. Why "super" and not "extends". Firstly it's always more useful to define

useful function at the top of the hierarchy that can be used by the child classes. And secondly if we have a "Person" type for the first "Predicate" then

we know that a Predicate object of a super class can be safely applied since a Person class will inherit all the attributes of it's base classes. As an example

suppose we tried to use the "extends" keyword in the Predicate definition instead of extends.

default MyPredicate<T> or(MyPredicate<? extends T> other) {

Objects.requireNonNull(other);

return (t) -> test(t) || other.test(t);

}

The above will not compile because T in our case is "Person" . And when we call "other.test(t)" we get a compiler error.

error: incompatible types: T cannot be converted to CAP#1

other.test( t) ;

We are violating the "PECS" rule of

PECS

Producer extends, Consumer super

In this case we can read a value from the "other" in the form of the type "T" but we can't let it consume some value of "T". The

reason is that the

constructor takes a "T" type but the "t" could be a subtype of type "T" . Similarly the "other" object could be a subtype of "T"

and first "t" may not

be compatible with that object. As an example the following program shows how this can happen:

class Person {}

class Manager extends Person {}

class Supervisor extends Person {}

class MyPredicate<T>

{

T t;

public void test(T targ )

{

t = targ ;

}

public void or(MyPredicate<? extends T> other)

{

//(t) -> test(t) || other.test(t)

//other.test( t) ;

}

}

public class generic7

{

public static void main( String args[] )

{

MyPredicate<Person> obj1 = new MyPredicate<Person>() ;

obj1.test( new Manager() ) ;

obj1.method( new MyPredicate<Supervisor>() ) ;

}

}


The "or" condition can be used as:

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------------------

/*

Illustrating the functional interfaces

*/

class Person

{

public enum Sex {

MALE, FEMALE

}

String name;

//LocalDate birthday;

Sex gender;

String emailAddress;

int age ;

public int getAge()

{

return age ;

}

public Sex getGender()

{

return gender ;

}

public void printPerson()

{

System.out.println( "Printing Person:" + "Name = " + name ) ;

}

}

//----------------------------------------------------------------------------------------------------------

interface CheckPerson

{

boolean test(Person p);

}

//----------------------------------------------------------------------------------------------------------

public class func2

{

//----------------------------------------------------------------------------------------------------------

public static void printPersons(List<Person> roster, Predicate<Person> tester)

{

//tester = tester.negate() ;

tester = tester.or( (personObj) -> personObj.getAge() <= 20 ) ;

for (Person p : roster)

{

if (tester.test(p))

{

p.printPerson();

}

} //for

}

//----------------------------------------------------------------------------------------------------------

public static void main( String args[] )

{

System.out.println( "Inside main" ) ;

List<Person> roster1 = new Vector<Person>() ;

Person p1= new Person() ;

p1.name = "Ajay" ; p1.age = 20 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

p1= new Person() ;

p1.name = "Timothy" ; p1.age = 24 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

printPersons( roster1, (personObj) -> personObj.getAge()> 20 ) ;

}

//----------------------------------------------------------------------------------------------------------

}

//----------------------------------------------------------------------------------------------------------

Output:

Inside main

Printing Person:Name = Ajay

Printing Person:Name = Timothy

One Predicate object has greater than 20 age and the other predicate has the condition less than or equal to and so all the Person objects are printed out.

Consumer

The following code shows how the "Consumer" interface can be used. This interface takes a single argument and does not return anything.

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------------------

/*

Illustrating the functional interfaces

*/

//----------------------------------------------------------------------------------------------------------

public class consumer1

{

public static void main( String args[] )

{

Consumer<String> consumer1 = (Obj1) -> System.out.println(Obj1 ) ;

consumer1.accept( "A test String." ) ;

}

}

Supplier

The "Supplier" interface has a method that produces something. Ex:

import java.util.function.Supplier;

class mySupply implements Supplier<String>

{

public String get()

{

return( "Some Sample String" ) ;

}

}

public class supplier1

{

public static void main(String[] args)

{

Supplier<String> supplierObj = ()-> "java2s.com";

System.out.println(supplierObj.get());

//Nothing to stop us from defining a regular class.

supplierObj = new mySupply() ;

System.out.println(supplierObj.get());

}

}

Default Methods in Interfaces

The introduction of lamda expressions in jdk 1.8 involved changing the existing interfaces. The problem with changing interfaces is of course that the introduction of a method in an interface breaks all the existing classes. With default methods in the interface the existing classes do not have to be modified. If a class implements such an interface but not the new method and attempts to call the method then the default implementation is called.

Suppose we had an interface A and a class implementing it:

interface A

{

void foo() ;

}

class ClassImplementsA implements A

{

public void foo()

{

System.out.println( "Method foo.");

}

}

and we wanted to add a method "foo1()" to it.We can do so by:

interface A

{

void foo() ;

default void foo1()

{

System.out.println( "Method foo1.");

}

}

If an instance of "ClassImplementsA" calls the "foo1()" method then the default implementation is called. If a new class is created then that class can either use the default implementation or provide it's own.

class ClassImplementsA implements A

{

public void foo()

{

System.out.println( "Method foo.");

}

//Can override the default implementation if need be.

public void foo1()

{

System.out.println( "Method foo1. Inside class ClassImplementsA");

}

}

If there are 2 interfaces that provide a default implementation for a method by the same name and a class implements both the interfaces without providing an implementation of the common method then the code does not compile as the compiler can't figure out what method to call.

interface A

{

default void foo1()

{

System.out.println( "Method foo1. Inside interface A");

}

}

//----------------------------------------------------------------------------------------------------------

interface B

{

default void foo1()

{

System.out.println( "Method foo1. Inside interface B");

}

}

//----------------------------------------------------------------------------------------------------------

class ClassImplementsAB implements A,B

{

//which foo1 is called ?

//default2.java:22: error: class ClassImplementsA inherits unrelated defaults for foo1() from types A and B

//class ClassImplementsA implements A,B

}

To correct the problem we have to override the default implementation:

class ClassImplementsAB implements A,B

{

//which foo1 is called ?

//default2.java:22: error: class ClassImplementsA inherits unrelated defaults for foo1() from types A and B

//class ClassImplementsA implements A,B

//Override the default implementation

public void foo1()

{

//A.super.foo1();

//B.super.foo1();

System.out.println( "Method foo1. Inside class ClassImplementsAB");

}

}

Inside the implementation of the method the specific default methods of the interfaces can be called by using the notation:

"Interface.super.defaultmethod"

Method References

In a Lambda expression we can write the code to be executed. However we can also specify that the code that exists as a method in some class. We can even specify the code of a constructor to be used as a method.

Static methods

import java.util.function.Function;

//Using a static method reference

public class static1

{

public static void main(String[] argv)

{

// Using a lambda expression

Function<Integer, String> func1 = x -> { return("some sample string."); } ;

System.out.println(func1.apply(10));

// Using a lambda expression that uses a static method

Function<Integer, String> func2 = x -> Integer.toBinaryString(x);

System.out.println(func1.apply(10));

// Using a method reference to a static method in the shortest form

Function<Integer, String> func3 = Integer::toBinaryString;

System.out.println(func2.apply(10));

}

}

In the first "func0" object we are assigning a Lambda expression to the object. The interface is of type "Function" in which the

input parameter is an Integer and the output result is a String. The second "func2" object takes a static method as the code. We are

specifying the reference method as "Class.method" along with the parameter.

For "func3" object we have the shortest form of the static method.

The static method is written as "Class:method" . The method should have the same signature as the functional object that it

is assigned to . In this case the method takes an Integer and returns a String.

Another example of static method reference:

import java.util.* ;

import java.util.function.* ;

public class method_ref1

{

/**

* @param args the command line arguments

*/

public static void main(String[] args) {

List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16);

List primeNumbers = method_ref1.findPrimeNumbers(numbers,

//(number) -> method_ref1.isPrime((int) number)

method_ref1::isPrime // Sometimes the compiler is not able to figure out the arguments

);

System.out.println("Prime Numbers are " + primeNumbers);

}

public static boolean isPrime(int number) {

if (number == 1) {

return false;

}

for (int i = 2; i < number; i++) {

if (number % i == 0) {

return false;

} }

return true;

}

public static List findPrimeNumbers(List list, Predicate<Integer> predicate) {

List sortedNumbers = new ArrayList();

list.stream().filter(

(i) -> (predicate.test((int)i)) //Line A

).forEach(

(i) -> {

sortedNumbers.add(i);

});

return sortedNumbers;

}

}

The above is another example where the static function is defined in the class and has the same signature as a Predicate functional interface. Notice in "Line A" we need to type case the argument to

be of type "int". The Java compiler need some help.

Constructors

We can also have constructors as method references. As an example:

Optional

Streams

Up to now all that has been done with Lambdas could have been done ( although in less than elegant way ) by using Interface and classes. However the real power of Lambdas comes into play with a new package "java.util.stream" .

One of the problems with collection classes is that either they are not thread safe and if we use synchronized apis to make them thread safe they are inefficient. With Streams we can have a collection do operations in parallel without worrying about the inner details.

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------------------

/*

Illustrating the functional interfaces

*/

class Person

{

public enum Sex {

MALE, FEMALE

}

String name;

//LocalDate birthday;

Sex gender;

String emailAddress;

int age ;

public int getAge()

{

return age ;

}

public Sex getGender()

{

return gender ;

}

public void printPerson()

{

System.out.println( "Printing Person:" + "Name = " + name ) ;

}

}

//----------------------------------------------------------------------------------------------------------

public class stream1

{

public static void main( String args[] )

{

List<Person> roster1 = new Vector<Person>() ;

Person p1= new Person() ;

p1.name = "Ajay" ; p1.age = 20 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

p1= new Person() ;

p1.name = "Timothy" ; p1.age = 24 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

OptionalDouble average = roster1

.parallelStream()

.mapToInt(Person::getAge)

.average() ;

System.out.println( "Average=" + average ) ;

}

}

The above program obtains the average age of people in the list "roster1". A parallel stream is used to perform the computation in parallel. We will start with the basics and come back to the above program later on.

A new package "java.util.stream" has been defined in JDK 8 that contains the Stream interface and it's relevant classes. The collection framework has also been updated to incorporate the Stream classes. Let us create a simple example that allows us to go through each "Person" in our "Roster" and a function that will print out some information about the "Person" ( stream1.java) .

import java.util.* ;

class Person

{

public enum Sex {

MALE, FEMALE

}

String name;

//LocalDate birthday;

Sex gender;

String emailAddress;

int age ;

public int getAge()

{

return age ;

}

public Sex getGender()

{

return gender ;

}

public void printPerson()

{

System.out.println( "Printing Person:" + "Name = " + name ) ;

}

}

public class stream1

{

public static void main( String args[] )

{

System.out.println( "Inside main" ) ;

List<Person> roster1 = new Vector<Person>() ;

Person p1= new Person() ;

p1.name = "Ajay" ; p1.age = 20 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

p1= new Person() ;

p1.name = "Timothy" ; p1.age = 24 ; p1.gender = Person.Sex.MALE ;

roster1.add( p1 ) ;

roster1.stream().forEach(personObj -> System.out.println(personObj.getGender()) ) ;

roster1.stream().filter(personObj -> {

System.out.println(personObj.getGender()) ;

return ( personObj.getAge() > 18 ) ;

}) ;

}

}

Output:

Inside main

MALE

MALE

From the "List" object we obtain a "Stream" and then call operations on it. In the first operation "forEach" takes a "Consumer" object. The "Consumer" interface is defined as having a method "accept" that takes a single argument and does not return a result. In our example we take the "Person" object and print the gender out for each person. In the second statement the "filter" method is called. This method takes a "Predicate" object but nothing is printed on the screen. The "Stream" interface has 2 types of methods(eager and lazy). In order to avoid iterating multiple times over a collection the builder ( lazy ) methods first build and store all the necessary information( filters, sorters ) and then an eager method at the end can be used to return the result.

The above examples have shown how we can use Streams.

Performance

First test focuses on lambda expression and creating objects out of the expression. The attached file is "perf1.java" .

//-----------------------------------------------------------------------------------------------------------

interface i1

{

void foo() ;

}

public class perf1

{

public static void main(String args[] )

{

int numberTimesToCreate = 1000000 ;

long lStartTime = System.currentTimeMillis() ;

for( int j1=0 ; j1<numberTimesToCreate ; j1++ )

{

i1 i1Object = new i1()

{

public void foo()

{

System.out.println( "Inside method foo." ) ;

}

} ;

} //for

long lEndTime = System.currentTimeMillis();

long difference = lEndTime - lStartTime;

System.out.println("Elapsed milliseconds: " + difference );

System.out.println("Elapsed seconds: " + difference/1000 );

lStartTime = System.currentTimeMillis() ;

for(int j1=0 ; j1<numberTimesToCreate ; j1++ )

{

i1 i1Object = () -> System.out.println( "Inside method foo." ) ;

} //for

lEndTime = System.currentTimeMillis();

difference = lEndTime - lStartTime;

System.out.println("Elapsed milliseconds: " + difference );

System.out.println("Elapsed seconds: " + difference/1000 );

}

}

//-----------------------------------------------------------------------------------------------------------

In this file we have an interface "i1" with a single method "foo" . We first create anonymous objects and then we create objects from lambda expression. In both cases the method code is the same. The results on a Linux machine:

Elapsed milliseconds: 8

Elapsed seconds: 0

Elapsed milliseconds: 75

Elapsed seconds: 0

The anonymous way of creating objects is 8-9 times faster on average.

The next example I made uses the "Stream" object. The file is "perf2.java" .

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------

class Employee

{

public static enum Gender {

MALE, FEMALE

}

public long id ;

public String name ;

public Gender gender ;

public Employee(long id, String name, Gender gender)

{

this.id = id;

this.name = name;

this.gender = gender;

}

public static List<Employee> persons()

{

int numberTimesToCreate = 1000000 ;

List<Employee> persons = new Vector<Employee>() ;

for( int i1=0 ; i1<numberTimesToCreate ; i1++ )

{

Random rn = new Random();

int id = rn.nextInt(1000000) + 1;

persons.add(new Employee( id, "Jake" + id , Gender.MALE ) )

;

}

return persons;

}

}

//----------------------------------------------------------------------------------------------

public class perf2

{

public static void main(String args[] )

{

int numberTimesToCreate = 1000000 ;

int filterLimit = 500000 ;

long lStartTime = System.currentTimeMillis() ;

long countOfPeople = Employee.persons().stream().filter( personObj -> (personObj.id < filterLimit ) ).count() ;

System.out.println("countOfPeople :" + countOfPeople ) ;

long lEndTime = System.currentTimeMillis();

long difference = lEndTime - lStartTime;

System.out.println("Elapsed milliseconds: " + difference );

System.out.println("Elapsed seconds: " + difference/1000 );

lStartTime = System.currentTimeMillis() ;

countOfPeople = 0 ;

List<Employee> listPersons = Employee.persons() ;

for(Employee personObj1 : listPersons )

{

if ( personObj1.id < filterLimit )

countOfPeople++ ;

} //for

System.out.println("countOfPeople :" + countOfPeople ) ;

lEndTime = System.currentTimeMillis();

difference = lEndTime - lStartTime;

System.out.println("Elapsed milliseconds: " + difference );

System.out.println("Elapsed seconds: " + difference/1000 );

}

}

//----------------------------------------------------------------------------------------------

In this program we are creating a million "Employee" objects and then creating a stream from this list. We are then using a "filter" to only select employees whose id is less than 500,000. The id is assigned a random value when creating the list.

Using Lambdas we use the filter and count functions whereas for the old style way we iterate through the list and use the same Predicate condition as an if condition and count the people that satisfy the condition. We can see in the output results that the old way of iterating is still faster than using lambdas.

Run 1:

countOfPeople :499297

Elapsed milliseconds: 1335

Elapsed seconds: 1

countOfPeople :500256

Elapsed milliseconds: 1091

Elapsed seconds: 1

Run 2:

countOfPeople :500928

Elapsed milliseconds: 1392

Elapsed seconds: 1

countOfPeople :499397

Elapsed milliseconds: 1092

Elapsed seconds: 1

Run 3:

countOfPeople :499689

Elapsed milliseconds: 1341

Elapsed seconds: 1

countOfPeople :499946

Elapsed milliseconds: 1142

Elapsed seconds: 1

Let's convert the above code to use the "ParallelStream " feature.

import java.util.* ;

import java.util.function.* ;

//----------------------------------------------------------------------------------------------

class Employee

{

public static enum Gender {

MALE, FEMALE

}

public long id ;

public String name ;

public Gender gender ;

public Employee(long id, String name, Gender gender)

{

this.id = id;

this.name = name;

this.gender = gender;

}

public static List<Employee> persons()

{

int numberTimesToCreate = 1000000 ;

List<Employee> persons = new Vector<Employee>() ;

for( int i1=0 ; i1<numberTimesToCreate ; i1++ )

{

Random rn = new Random();

int id = rn.nextInt(1000000) + 1;

persons.add(new Employee( id, "Jake" + id , Gender.MALE ) )

;

}

return persons;

}

}

//----------------------------------------------------------------------------------------------

public class perf5b

{

public static void main(String args[] )

{

int numberTimesToCreate = 1000000 ;

int filterLimit = 500000 ;

long lStartTime = System.currentTimeMillis() ;

long countOfPeople = Employee.persons().parallelStream().filter( personObj -> (personObj.id < filterLimit ) ).count() ;

System.out.println("countOfPeople :" + countOfPeople ) ;

long lEndTime = System.currentTimeMillis();

long difference = lEndTime - lStartTime;

System.out.println("Elapsed milliseconds: " + difference );

System.out.println("Elapsed seconds: " + difference/1000 );

lStartTime = System.currentTimeMillis() ;

countOfPeople = 0 ;

List<Employee> listPersons = Employee.persons() ;

for(Employee personObj1 : listPersons )

{

if ( personObj1.id < filterLimit )

countOfPeople++ ;

} //for

System.out.println("countOfPeople :" + countOfPeople ) ;

lEndTime = System.currentTimeMillis();

difference = lEndTime - lStartTime;

System.out.println("Elapsed milliseconds: " + difference );

System.out.println("Elapsed seconds: " + difference/1000 );

}

//----------------------------------------------------------------------------------------------

}

Results:

With parallel streams.

countOfPeople :500083

Elapsed milliseconds: 2422

Elapsed seconds: 2

countOfPeople :499701

Elapsed milliseconds: 941

Elapsed seconds: 0