Java 8 - Les interfaces fonctionnelles

Alexis Seigneurin - @aseigneurin

Ippon Technologies - @ippontech

 

(Flèches droite/gauche et haut/bas pour naviguer, ESC pour tout afficher)

Interfaces fonctionnelles ?

Interface fonctionnelle

  • Interface avec une seule méthode abstraite
  • Méthodes par défaut = non-abstraites

Annotation

  • @java.lang.FunctionalInterface

@FunctionalInterface
public interface ExampleInterface {

    void doSomething();

    default int methodWithDefaultImpl() {
        return 0;
    }

}
        		

Vérification par le compilateur


Unexpected @FunctionalInterface annotation

com.seigneurin.ExampleInterface is not a functional interface

multiple non-overriding abstract methods found in interface com.seigneurin.ExampleInterface
						

Retour en arrière

Java < 8

  • Callback = classes anonymes

Button btn = new Button();
btn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println("Hello World!");
    }
});
        		

Exemple

  • On veut parser des noms : NameParser
  • Création du nom déléguée : Creator

public class NameParser {
    public <T> T parse(String name, Creator<T> creator) {
        String[] tokens = name.split(" ");
        String firstName = tokens[0];
        String lastName = tokens[1];
        return creator.create(firstName, lastName);
    }
}

public interface Creator<T> {
    T create(String firstName, String lastName);
}

public class Name {

   private String firstName;
   private String lastName;

   public Name(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }

   public String getFirstName() { return firstName; }
   public String getLastName() { return lastName; }

}
        		

Classe anonyme


NameParser parser = new NameParser();

Name res = parser.parse("Eric Clapton", new Creator<Name>() {
    @Override
    public Name create(String firstName, String lastName) {
        return new Name(firstName, lastName);
    }
});
        		

Java 8

@FunctionalInterface

  • Annotation optionnelle

@FunctionalInterface
public interface Creator<T> {
    T create(String firstName, String lastName);
}
        		

Appel : une méthode...

  • Avec 2 paramètres de type String
  • Retour de type générique

Code appelé...

  • Méthode create() simulée par le compilateur

Référence vers un constructeur

  • Notation : "Classe::new"

NameParser parser = new NameParser();

Name res = parser.parse("Eric Clapton", Name::new);
        		

Référence vers une méthode statique

  • Notation : "Classe::méthode"

public class Factory {
    public static Name createName(String firstName, String lastName) {
        return new Name(firstName, lastName);
    }
}
        		

NameParser parser = new NameParser();

Name res = parser.parse("Eric Clapton", Factory::createName);
        		

Référence vers une méthode d’instance

  • Notation : "instance::méthode"

public class Factory {
    public Name createName(String firstName, String lastName) {
        return new Name(firstName, lastName);
    }
}
        		

Factory factory = new Factory();
NameParser parser = new NameParser();

Name res = parser.parse("Eric Clapton", factory::createName);
        		

Expression lambda


Name res = parser.parse("Eric Clapton", (s1, s2) -> new Name(s1, s2));
        		

Name res = parser.parse("Eric Clapton", (s1, s2) -> Factory.createName(s1, s2));
        		

Factory factory = new Factory();
Name res = parser.parse("Eric Clapton", (s1, s2) -> factory.createName(s1, s2));
        		

Interfaces fonctionnelles du JDK

Package java.util.function

  • Consumer<T>
    • void accept(T);

  • Function<T, R>
    • R apply(T);

  • Supplier<T>
    • T get();

  • Predicate
    • boolean test(T);

Plusieurs méthodes ?

Java < 8


public interface Operation<T>
{
    T function();
    void onSuccess(T res);
    void onError(Exception ex);
}
        		

public <T> void doSomething(Operation<T> operation) {
    try {
        T res = operation.function();
        operation.onSuccess(res);
    } catch (Exception ex) {
        operation.onError(ex);
    }
}
        		

Appel très verbeux


doSomething(new Operation<Object>() {
    @Override
    public Object function() {
        return 42;
    }
    @Override
    public void onSuccess(Object res) {
        System.out.println(res);
    }
    @Override
    public void onError(Exception ex) {
        System.err.println("Error: " + ex.getMessage());
    }
});
        		

Java 8

  • Stratégie = découper l'interface

public <T> void doSomething(Supplier<T> function, Consumer<T> onSuccess, Consumer<Exception> onError) {
   try {
       T res = function.get();
       onSuccess.accept(res);
   } catch (Exception ex) {
       onError.accept(ex);
   }
}
        		

Appel simplifié


doSomething(
    () -> 42,
    System.out::println,
    ex -> System.err.println("Error: " + ex.getMessage()));
        		

Questions ?

@aseigneurin

Code : github.com/aseigneurin/pres-java8-breizhcamp

Slides : aseigneurin.github.io/downloads/pres-java8-breizhcamp