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
Questions
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
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.
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.
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:
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.
3 stages of stream pipeline operations
Create stream.
Specify intermediate operations for transforming the initial stream.
3 common ways of creating a stream
someList.stream()
Stream.of(arrayOfObjects) not primitives
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)
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.