Avant Java 8, il n’existait que deux types de références, des valeurs primitives (char c = ‘C’ ;) et des références vers des objets (String s = new String(“Hello”);)
Depuis Java 8, il est possible d’avoir des références vers des morceaux de codes anonymes (des closures) : les lambdas

Une lambda est la redéfinition d’une méthode méthode abstraite d’une interface fonctionnelle (elle doit répondre à la signature de la méthode concernée) sans avoir à faire une classe anonyme, donc gain de ligne de code et de visibilité.
On utilise le nouvel opérateur ->

ex simple:

1
() -> 137;

ex avec une interface fonctionnelle

1
2
3
4
5
6
7
package com.cyrgue.lambdas;
@FunctionalInterface
public interface StudentCriterion {
public boolean test(Student student);
}

StudentCriterion criteria = (s) -> s.getGpa() > 3.0

Donc pour pouvoir utiliser une lambda, on doit avoir

  • une interface avec seulement une seule méthode abstraite (peux avoir d’autre méthode)à
  • et on veut implementer cette methode

Il existe

  • des expressions lambda

    1
    s -> s.getGpa() > 2.7
  • blocked lambda

    1
    s -> { return s.getGpa() > 2.7;}

return est utilisé seulement dans une blocked lambda (le ; aussi)

Predicate

Api Interface Predicate

Represents a predicate (boolean-valued function) of one argument.
This is a functional interface whose functional method is test(Object).

Predicate has multiple method but ONLY ONE is Abstract

Exemple

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package lambdas;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

//interface StudentCriterion {
// boolean test(Student s);
//}

class SmartStudent implements /*StudentCriterion*/ Predicate<Student> {
@Override
public boolean test(Student s) {
return s.getGpa() > 3.0;
}
}

class LongName implements /*StudentCriterion*/ Predicate<Student> {
@Override
public boolean test(Student s) {
return s.getName().length() > 4;
}
}

class Student {
private String name;
private double gpa;
private int balance;

public Student(String name, double gpa, int balance) {
this.name = name;
this.gpa = gpa;
this.balance = balance;
}

public String getName() {
return name;
}

public double getGpa() {
return gpa;
}

public int getBalance() {
return balance;
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", gpa=" + gpa +
", balance=" + balance +
'}';
}
}
public class School {
public static void showAllStudents(List<Student> ls) {
for (Student s : ls) {
System.out.println(s);
}
System.out.println("-------------------------");
}

public static List<Student> findStudents(List<Student> ls,
Predicate<Student> crit) {
// StudentCriterion crit) {
List<Student> out = new ArrayList<>();
for (Student s : ls) {
if (crit.test(s)) {
out.add(s);
}
}
return out;
}

// public static List<Student> findSmartStudents(List<Student> ls,
// double threshold) {
// List<Student> out = new ArrayList<>();
// for (Student s : ls) {
// if (s.getGpa() > threshold) {
// out.add(s);
// }
// }
// return out;
// }
//
// public static List<Student> findLongNameStudents(List<Student> ls,
// int threshold) {
// List<Student> out = new ArrayList<>();
// for (Student s : ls) {
// if (s.getName().length() > threshold) {
// out.add(s);
// }
// }
// return out;
// }
//
// public static void showAllSmartStudents(List<Student> ls) {
// for (Student s : ls) {
// if (s.getGpa() > 3.0) {
// System.out.println(s);
// }
// }
// System.out.println("-------------------------");
// }
//
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Fred", 3.7, 1_000));
students.add(new Student("Jim", 2.7, 20_000));
students.add(new Student("Sheila", 3.9, 5_000));
students.add(new Student("Alice", 2.9, 12_000));
showAllStudents(students);
// showAllStudents(findSmartStudents(students, 2.8));
// showAllStudents(findLongNameStudents(students, 4));
showAllStudents(findStudents(students, new SmartStudent()));
showAllStudents(findStudents(students, new LongName()));

showAllStudents(findStudents(students,
/*
* MUST be implementation of StudentCriterion
* MUST be an object :)
*
* StudentCriterion has exactly one abstract method
* and we ONLY want to implement that one method
* and (language rule) StudentCriterion is an INTERFACE
* then, we can use a lambda...
*
* Just implement the method as a skeleton
* */
// (Student s) -> { return s.getGpa() > 2.7; }
// (s) -> { return s.getGpa() > 2.7; }
// s -> { return s.getGpa() > 2.7; }
// s -> /*{ return */s.getGpa() > 2.7/*; }*/
s -> s.getGpa() > 2.7
));

showAllStudents(findStudents(students, s -> s.getGpa() < 3.8));

Predicate<Student> firstHalf = s -> s.getName().charAt(0) <= 'M';

boolean b = ((Predicate<Student>)(s -> s.getName().charAt(0) <= 'M'))
.test(new Student("Albert", 2.2, 2_000));
}
}



// print students with more than 10_000 debt
// print students with less than 15_000 debt and gpa > 3
//
//A) I did it! (both)
//B) I did one
//C) Total failure!!!
//D) oops, I made tea!

Importance du Context

Il est important de specifier le Type de Predicate utilisé, Object n’est pas suffisant (ici Student)

Exercice

Which compile?

A) Predicate<Student> ps = s => { return s.getGpa() >3; } 
B) Predicate<Student> ps = s -> { return s.getGpa() > 3; }
C) Predicate<Student> ps = -> { return s.getGpa() >3; } // missing argument

Response : B
A : Error : Operator :JavaScript, Scala use => Java uses ->
C : Error : missing argument

Which compile?

A) Object ps = s -> { return s.getGpa() >3; } 
B) Predicate<Student> ps = s -> return s.getGpa() >3;
C) Predicate<Student> ps = s -> { s.getGpa() >3 }
D) none of the above

Response : D
A : NO CONTEXT
B : Error : no return in expression lambda
C : Error : missing return in blocked lambda

Anonymous Inner Classes

Reason to use it : One time inner code
Inner classes have a full access to all methods and instance variables of surrounding class, including private ones

Separate Class

  • Advantages :
    - Flexible : Can pass arguments to constructor
    - More reusable : loosely coupled
    
  • Disadvantage :
    - Need extra step to call methods in main app         
    

Lambdas

  • Full access to code from surrounding class
  • No confusion about meaning of “this”
  • Much more concise, succinct and readable
  • Encourage a functional programming style

    No instance variables

Review of Building Genericized Methods and Classes

Motivation and Overview

Advantages

  • Concise and Expressive (/ a inner class)
  • Encourage functional programming
  • Support Streams (streams are wrappers around data sources(arrays, collections…) (map/reduce/filter), use lazy evaluation and can be made parrallel automatically by compiler

Type inferencing

Types in argument list can usually be omitted. Java checks at compile time the type in interface (not at runtime), so not slower

Omitting parens ()

If method takes a single argument, then the parens () in the argument list are optional

The @FunctionalInterface Annotation

-> Catches errors at compile time (to specify the interface must have only one abstract method)
-> Expresses design intent
-> But not technically required

Method references

ClassName::staticMethodName
variable:instanceMethod
Class:instanceMethod
ClassOrType::new (constructor)

The type is defined by the Context

Supplier
Constructor Reference
Array Constructor Reference

Lambda Variable Scoping in Lambdas

  • this variable refers to the outer class, not to the anonymous inner class that the lambda is turned into

4 - Lambda Building Blocks in java.util.function

4.1 - Lambda Building Blocks

4.2 - Simply-Typed Building Blocks

  • java.util.function : many reusable interfaces
    • Simply typed interfaces : IntPredicate, LongUnaryOperator
    • Generically typed Interfaces : Predicate, Function, Consumer, Supplier, BinaryOperator

4.3 - Predicate

boolean test (T t)

Used to filter list, to have a subset of the list (or use the built-in filter method in Stream)

4.4 - Function

R apply(T t)

Transform a value or collection of values

4.5 - Other Generic Building Blocks

  • BinaryOperator : T apply (T t1, T t2)
  • Consumer : void accept(T t)
  • Supplier : T get()

Programming with Java 8 Lambdas and Streams - LIVE ONLINE TRAINING

Difference between OOP () and functional Programing
Java 8 combined :

  • Object-oriented Programing (imperative paradigm, sequential code), hierarchical data abstraction, state is encapsulated by methods thats perform itertive statements
  • functinal programming is a declarative paradigm
    (focus on what instead of how)
    Pure function
    Function composition : the output serves as imput for next one
    Changing State & mutable data ARE DISCOURAGED
    Focus on Immutable objects 
    

Overview of Lambda Expressions

A Lambda expression is an unnamed block of code (with optional parameters) that ca be stored , passed around and executed later (in another context, another thread…)

Values of private fieds can be updated but not params

Make the code more generable

Method References

:: 

Ref

https://github.com/douglascraigschmidt/LiveLessons

Other

Functional Programming