// Interface fonctionnelle = une seule méthode abstraite
@FunctionalInterface // pas obligé mais informe le compilo
interface MonInterface {
// Méthode abstraite
public boolean f();
// Méthode statique, n'est pas abstraite
public static void uneMethodeStatique() {
}
// Factory
public static MonInterface create() {
return new MonInterface() {
public boolean f() {
return true;
}
};
}
// Implémentation par défaut
default public boolean g() {
return !f();
}
}
interface A {
default public String f() {
return "A";
}
}
interface B {
default public String f() {
return "B";
}
}
class K implements A, B {
// il faut redéfinir f pcq ya ambiguité entre le f de A et celui de B
public String f() {
return "";
}
public String toString() {
return A.super.f() + B.super.f();
}
}
class MyClass implements Runnable {
@Override
public void run() {
System.out.println("hello");
}
}
public class Main {
public static void main(String[] args) {
Runnable r = new MyClass();
Thread t = new Thread(r);
t.start();
}
}
Objet foncteur
f : \begin{aligned} &\N^{2} \ra \N \\\\ &x, y \mapsto x+y \end{aligned}
Interface fonctionnelle
NE définit qu’une et une seule méthode abstraite.
Possibilité de faire des :
- statiques (\RA usage : résoudre le problème du factoring)
- implémentation par défaut (\RA usage : résoudre le problème des interfaces riches, aka beaucoup de méthodes liées les une aux autres). Cette implémentation peut être écrasée au besoin dans les classes implémentant l’interface.
Lambda expression = fonction anonyme.
import java.util.Comparator;
interface Operator {
public int calcul(int i, int j);
}
class Adder implements Operator {
@Override
public int calcul(int i, int j) {
return i + j;
}
}
interface I {
public int f(int i);
}
interface HIHI {
public Object create();
}
public class Main {
public static void util(Operator o, int a, int b) {
System.out.println(o.calcul(a, b));
}
public static void main(String[] args) {
// Utilisation de la classe
Operator add = new Adder();
util(add, 4, 5);
// On override la classe pour faire notre calcul
Operator add2 = new Operator() {
@Override
public int calcul(int i, int j) {
return i+j;
}
};
util(add2, 4, 5);
// On utilises une lambda expr pour faire notre calcul
util((a, b) -> a+b, 4, 5); /* Ne va fonctionner que si `Operator`
* est une interface fonctionnelle. */
// Même logique, on associe ça à f() parce que f() est la seule méthode de l'interface
// Càd on est dans ue interface fonctionnelle.
Operator mult = (int x, int y) -> x*y; // Ici il n'y a pas d'instanciation d'objet !
// Types non obligatoire évidemment, l'inférence de type fonctionne
Operator mult2 = (x, y) -> x*y; // Ici il n'y a pas d'instanciation d'objet !
Operator add3 = (x, y) -> Integer.sum(x, y); // J'appelle la méthode de façon idiote
Operator add4 = Integer::sum; /* Alors je peux directement appeller la méthode comme ça
* = référence de méthode statique */
Comparator<Integer> c = (i1, i2) -> i1.compareTo(i2); // J'appelle encore de façon bête
Comparator<Integer> c2 = Integer::compareTo; /* très smart ce compilo... mmmhh...
* = référence de méthode d'instance */
// Toujours la même logique
Integer deux = 2;
I ii = (x) -> deux.compareTo(x);
I ii2 = deux::compareTo; /* référence de méthode d'instance désignée
* = "je veux la méthode sur cet objet là !" */
// S'il n'y a qu'UN paramètre, pas besoin de parenthèses
I i = (x) -> x + 1;
// On peut aussi mettre un bloc d'instruction, dans ce cas là il faut un return
I i2 = x -> { int tmp = x; return tmp + 1; };
HIHI h = () -> new Object();
HIHI h2 = Object::new; // Appel au constructeur de "Object"
/* Fonctionne aussi si on a un constructeur avec plusieurs arguments !
* Donc c'est tout bon même avec les surcharges de constructeurs */
}
}
- Lambda expression \neq Objet
Trois choses dans le Java :
- Types primitifs
- Objets
- Lambda expression
Les plus importants Dans java.util.function
:
Consumer<T>
Supplier<T>
Function<T, R>
Predicate<T>
Consumer<T>
import java.util.List;
import java.util.function.Consumer;
public class Main {
// Cette classe itère dans une collection (d'Integer) et donne à manger à sysout::println
public static void util(List<Integer> l) {
for (var i : l) {
System.out.println(i);
}
}
// Abstraction de util
public static void utilabs(List<Integer> l, Consumer<Integer> c) {
for (var i : l) {
c.accept(i);
}
}
// Donc on peut écrire util comme ça :
public static void util2(List<Integer> l) {
utilabs(l, x -> System.out.println(x));
// que l'on peut aussi écrire :
utilabs(l, System.out::println);
}
public static void main(String[] args) {
Consumer<Integer> toScreen = System.out::println;
Consumer<Integer> toNetwork = x -> System.out.println(">" + x + "<");
Consumer<Integer> toScreenAndNetwork = x -> { toScreen.accept(x); toNetwork.accept(x); };
// que l'on peut aussi écrire...
Consumer<Integer> toScreenAndNetwork2 = toScreen.andThen(toNetwork);
List<Integer> l = List.of(1, 2, 3, 4);
utilabs(l, toScreenAndNetwork);
}
}