Java 8 

 

What are Lambdas ?

Functional Interface

Functional Interface vs Anonymous inner class

Method Reference

Constructor Reference

Functional Interfaces provided by java.util.functions.*

Default Methods and Multiple Inheritance

Overview of Streams

Core Stream methods

Optional class

Lazy evaluation

Intermingling Java 8 and Groovy

Agenda

 

  • Advantages of groovy over Java 7 ?
  • What are closures ?
  • Why use closures ?

Questions

 

  • Higher order functions
  • Immutibility
  • Pure Function

Pillars of functional programming

Imperative style of programming

Vs

Declarative style of programming

 

Anonymous functions

Can be assigned to a variable

Can be passed to functions

Can be returned from functions

What are lambdas?

 

Running the code in the separate thread.

Running the code multiple times.

Running the code at the right point in the algorithm.

Running the code when something happens.

Running the code when only necessary.

Deferred Execution

How to implement interface containing one abstract method?

public interface MyInterface {
    void display();
}

Old way

Create a class and implment the interface

Anonymous inner class

 

New way

lambdas

MyInterface myInterface=new MyInterface() {
            @Override
            public void display() {
                System.out.println("This is anonymous inner class");
            }
        };
myInterface.display();
MyInterface myInterface=()-> {
    System.out.println("This is anonymous inner class");
};
myInterface.display();

Functional Interface

Interface containing only one abstract method is known as Functional Interface.

public interface MyInterface {
    int operation(int a,int b);
}
MyInterface mylambda =(a,b)-> a+b;
System.out.println(mylambda.operation(2,3));

Goodies with lambdas

  • Type inferencing
  • Implied return value
  • Omitting parentheses

JDK Functional Interfaces

Runnable

Runnable runnable=()->System.out.print("Running in another thread");
new Thread(runnable).start();

Comparator

Employee e1=new Employee("Pulkit",40000,23);
Employee e2=new Employee("Jitu",20000,24);

List<Employee> list= Arrays.asList(e1, e2);
Collections.sort(list,(emp1,emp2)->{
   return emp1.getAge()-emp2.getAge();
 });

Exercise 1

Write the following a functional interface and implement it using lambda:

(1) First number is greater than second number or not

Parameter (int ,int ) Return boolean

(2) Increment the number by 1 and return incremented value

Parameter (int) Return int

(3) Concatination of 2 string

Parameter (String , String ) Return (String)

(4) Convert a string to uppercase and return

Parameter (String) Return (String)

Method References

It is not always necessary to use a lambda for the implementation of functional interface. Static and instance methods can also be used for thispurpose.

public interface MethodReferenceInterface {
    void display();
}
public class Dummy {

    static void staticMethod(){
        System.out.println("Static method");
    }

    void instanceMethod(){
        System.out.println("Instance Method");
    }

    public static void main(String[] args) {
        MethodReferenceInterface staticMethod=Dummy::staticMethod;
        staticMethod.display();
        MethodReferenceInterface instanceMethod=new Dummy()::instanceMethod;
        instanceMethod.display();
    }
}

Constructor Reference

Constructer reference is just like method reference except that the name of the method is new.

public interface DemoInterface {
    String myMethod(char [] chars);
}
 DemoInterface demoInterface = String::new;
 String str=demoInterface.myMethod(new char[]{'a', 'b', 'c', 'd'});
 System.out.println(str);

Exercise 2

 

  • Create a functional interface whose method takes 2 integers and return one integer.

  • Using (instance) Method reference create and apply add and subtract method and using (Static) Method reference create and apply multiplication method for the functional interface created.

  • Create an Employee Class with instance variables (String) name, (Integer)age, (String)city and get the instance of the Class using constructor reference  

Variable scoping in lambda

Lambdas are lexically scoped

They do not introduce a new level of scoping

• Implications

– The “this” variable refers to the outer class,

– There is no “OuterClass.this” variable

– Lambdas cannot introduce “new” variables with same

name as variables in method that creates the lambda

Variable Capture

  • Lambdas can interact with variables defined outside the body of the lambda.

  • Using variable outside the body of a lambda expression is called variable capture.

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
int var=10;
integerList.forEach(e->System.out.println(e+var));

Effectively Final Local Variables

Lambdas can refer to local variables that are not declared final (but are never modified)

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
int var=10;
var++;
// This line will generate error 
// because we are changing a variable before using it inside lambda expression
integerList.forEach(e->System.out.println(var));

this inside Anonymous Inner class

 

interface Demo{
    void display();
}

public class ThisDemo {

    Integer myInteger=1;

    public void myMethod(){
        Demo demo=new Demo() {
            Integer myInteger=2;
            @Override
            public void display() {
                System.out.println(this.myInteger);
            }
        };
        demo.display();
    }

    public static void main(String[] args) {
        new ThisDemo().myMethod();
    }
}

this inside lambda

interface Demo{
    void display();
}

public class ThisDemo {

    Integer myInteger=1;

    public void myMethod(){
        Demo demo=()-> {
            Integer myInteger=2;
            System.out.println(this.myInteger);
        };
        demo.display();
    }

    public static void main(String[] args) {
        new ThisDemo().myMethod();
    }
}

Lambda vs Anonymous Inner Class

 

  • Inner class can have multiple methods whereas lambdas can have only one method.

  • this points to the anonymous inner class object in case of anonymous inner class but in case of lambdas it points to enclosing object.

Java.util.function

Java.util.function has many reusable intefaces

(1) Simple typed interfaces

    DoublePredicate, IntConsumer, LongUnaryOperator

(2) Generically typed intefaces

     Consumer, Supplier, Predicate, Function

Exercise 3

Implement following functional interfaces from java.util.function using lambdas:

  • (1) Consumer

  • (2) Supplier

  • (3) Predicate

  • (4) Function

Lambda Expression lifecycle

  • Think of lambda expression as having a two stage lifecycle.

  • - Convert the lambda expression to a function.

  • - Call the generated function.

InvokeDynamic​

invokedynamic works with method handles to facilitate dynamic method invocation. A method handle is "a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation.

 

InvokeInterface

It is used to invoke methods at run time based on an interface type.

 

InvokeSpecial

invokespecial is used to invoke instance initialization methods (constructors) as well as private methods and methods of a superclass of the current class.

To know more about how lambdas are implemented behind the scenes watch:

A Peek Under the Hood By Brain Goetz

How Interface is different in Java 8 from Java 7

Java 7 and earlier Java 8
Can only have abstract methods Can have concrete methods and abstract methods
Cannot have static methods Can have static methods

Default methods in interface

If you want define a method inside an interface you can do it by making the method default.

interface inter1 {
    default void display(){
        System.out.println("inter1");
    }
}

public class DefaultMethods implements inter1 {
    public static void main(String[] args) {
        DefaultMethods defaultMethods=new DefaultMethods();
        defaultMethods.display();
    }
}

Static methods in interface

You can also define static method inside interface

interface inter1 {
    static void display(){
        System.out.println("inter1");
    }
}

public class DefaultMethods {
    public static void main(String[] args) {
        inter1.display();
    }
}

Overriding default method

interface inter1 {
    default void display(){
        System.out.println("inter1");
    }
}

public class DefaultMethods implements inter1 {
    public void display(){
        System.out.println("DefaultMethods");
    }

    public static void main(String[] args) {
        DefaultMethods defaultMethods=new DefaultMethods();
        defaultMethods.display();
    }
}

Multiple inheritance and default methods

interface inter1 {
    default void display(){
        System.out.println("inter1");
    }
}

interface child1 extends inter1{
    default void display(){
        System.out.println("child1");
    }
}

interface child2 extends inter1{
    default void display(){
        System.out.println("child2");
    }
}

public class DefaultMethods implements child1,child2 {
    public void display(){
        System.out.println("DefaultMethods");
    }

    public static void main(String[] args) {
        DefaultMethods defaultMethods=new DefaultMethods();
        defaultMethods.display();
    }
}

Exercise 4

(1) Create and access default and static method of an interface.

(2) Override the default method of the interface.

(3) Implement multiple inheritance with default method inside       interface.

Streams

  • Wrappers around data source.

  • Support many convinient and high-performance operations expressed with lambdas.

  • Streams do not store data.

  • Lazy evaluation.

  • Streams do not mutate their source.

3 stages of stream pipeline operations

  • Create stream.

  • Specify intermediate operations for transforming the initial stream.

  • Specify terminal operation to produce the result.

3 common ways of creating a stream

 

  • someList.stream()

  • Stream.of(arrayOfObjects) not primitives

  • Stream.of(val1,val2,....)    

Method types

Intermediate Methods

- These are methods that produce other Streams. These methods don’t get processed until there is some terminal method called.

- map (and related mapToInt, flatMap, etc.), filter, distinct,sorted, peek, limit, skip, parallel

Terminal Methods

- These methods terminates the stream.

- forEach, toArray, reduce, collect, min,max, count, anyMatch, allMatch, noneMatch, findFirst,findAny

-Short-circuit methods

- These methods cause the earlier intermediate methods to be processed only until the short-circuit method can be evaluated.

- anyMatch, allMatch, noneMatch, findFirst, findAny, limit, skip

Core stream methods

forEach(Consumer)

        Calling a lambda on each element of stream

List<Integer> list= Arrays.asList(1,2,3,4,5,6);
list.forEach(System.out::println);

map(Function)

   Transforming stream by passing each element through a Function

   Produces a new Stream that is result of applying a Function to each element of origin Stream

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        list.stream()
                .map(e -> e * 2)
                .forEach(System.out::println);

filter(Predicate)

Keep only the element that pass the predicate

list.stream()
             .filter(e -> e > 2)
             .forEach(System.out::println);

mapToInt

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        System.out.println(
        list.stream()
                .mapToInt(e -> e).max()
        );

Collect

toList()

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        System.out.println(
                list.stream()
                        .filter(e -> e > 3)
                        .collect(Collectors.toList())
        );

toSet()

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 5, 6, 7);
        System.out.println(
                list.stream()
                        .filter(e -> e > 3)
                        .collect(Collectors.toSet())
        );

joining()

List<String> list = Arrays.asList("abc","def");
        System.out.println(
                list.stream()
                        .collect(Collectors.joining(","))
        );

summingIn()

List<Integer> list = Arrays.asList(1, 2, 3, 4);
        System.out.println(
                list.stream()
                        .collect(Collectors.summarizingInt(x -> x * 2))
        );

averagingInt()

List<Integer> list = Arrays.asList(1, 2, 3, 4);
        System.out.println(
                list.stream()
                        .collect(Collectors.averagingInt(x -> x * 2))
        );

counting()

List<Integer> list = Arrays.asList(1, 2, 3, 4);
        System.out.println(
                list.stream()
                        .collect(Collectors.counting())
        );

toMap()

  List<Integer> list = Arrays.asList(1, 2, 3, 4);
        System.out.println(
                list.stream()
                        .collect(Collectors.toMap(e -> e, e -> e + " Number" ))
        );
  • Optional either stores a T or stores nothing. Useful for methods that may or may not find a value. New in Java 8.

  • - The value of findFirst of Stream is an Optional

  • - Some common operations on optional stream

  •  value.get()

  •  value.orElse(other)

  •  value.orElseGet(Supplier)

  •  value.isPresent()

Optional

Parallel Stream

By designating that a Stream be parallel, the operations are automatically done in parallel

Designating streams as parallel

- anyStream.parallel()

- anyList.parallelStream()

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
list.stream().parallel().forEach(System.out::println);

Exercise 5

  • Collect all the even numbers from an integer list.

  • Sum all the numbers greater than 5 in the integer list.

  • Find average of the number inside integer list after doubling it.

  • Find the first even number in the integer list which is greater than 3.

Java 8

By Pulkit Pushkarna

Java 8

  • 1,261