Nouveautés de Java 8

Soit le tableau d’instance de la classe Personne (prénom, nom, age) suivant :

List<Personne> personnes = Arrays.asList(new Personne("Pierre", "Durand", 20), 
                        new Personne("Marie", "Durand", 14),
                        new Personne("Albert", "Martin", 12));

Si la classe Personne redéfini equal et hashcode en fonction du nom et du prénom et que l’on souhaite trier le tableau par âge, il faut définir une classe qui implante l’interface Comparator<Personne> ou mieux utiliser une classe anonyme :

Collections.sort(personnes, new Comparator<Personne>() {
            @Override
            public int compare(Personne o1, Personne o2) {
                return o1.getAge() - o2.getAge();
            }
        });

A partir de Java 8 on peut utiliser des lambda expressions. Elles peuvent être vues comme des classes anonymes ayant une seule méthode dont le type de retour est inféré. Les type des paramètres peuvent aussi être inférés.

Une lambda est composée :

  • d’un ou ou de plusieurs nom de paramètres (alors entre parenthèse) éventuellement typés
  • du symbole →
  • d’un corps de fonction, s’il est réduit à une expression le return est facultatif.
Collections.sort(personnes, (o1, o2) -> o1.getPrenom().compareTo(o2.getPrenom()));

Java 8 définit le concept d’interfaces fonctionnelles (elles ont extactement une méthode). Elle permet de manipuler des lambda expressions ou des références vers des méthodes. Une interface peut être définie comme fonctionnelle avec l’annotation @FunctionalInterface. Un ensemble d’interface classiques est proposé dans le JDK :

  • Function<T,R> - takes an object of type T and returns R.
  • Supplier<T> - just returns an object of type T.
  • Predicate<T> - returns a boolean value based on input of type T.
  • Consumer<T> - performs an action with given object of type T.
  • BiFunction - like Function but with two parameters.
  • BiConsumer - like Consumer but with two parameters.
Function<String, String> at = (name) -> {
   return "@" + name;
};
for (Personne p : personnes) System.out.println(at.apply(p.getNom()));
Supplier<List> listFactory = ArrayList::new;
System.out.println("list factory : "+(listFactory.get() instanceof List));
Consumer<String> println = System.out::println;
println.accept("Consumer say Hello");

Retrouver des personnes avec un filtre.

En utilisant une classe générique pour la recherche :

public class Processor<T> {
    public List<T> find(Iterable<T> iterable, Predicate<T> predicate) {
        List<T> list = new ArrayList<>();
        for (T t : iterable)
            if (predicate.test(t))
                list.add(t);
        return list;
    }
}
 
Processor<Personne> personneProcessor = new Processor<>();
 
//avec une classe anonyme pour le critère
System.out.println(personneProcessor.find(personnes,
                new Predicate<Personne>() {
                    @Override
                    public boolean test(Personne p) {
                        return p.getNom().equals("Durand")
                                && p.getAge() >= 18
                                && p.getAge() <= 25;
                    }
                }));        

avec une lambda expression

        System.out.println(personneProcessor.find(personnes,
                p -> p.getNom().equals("Durand")
                        && p.getAge() >= 10
                        && p.getAge() <= 15
        ));

Un stream permet de représenter une séquence d’objets qui peut supporter l’exécution parallèle. La construction de stream peut être “lazzy”.

Un stream peut être créé au dessus d’une collection

List<Personne> personnes = new ArrayList<>();
personnes.add(new Personne("Pierre", "Durand", 20));
personnes.add(new Personne("Marie", "Durand", 14));
personnes.add(new Personne("Albert", "Martin", 12));
 
personnes.stream(); //Returns a sequential Stream with the collection as its source.
personnes.parallelStream(); //Returns a possibly parallel Stream with the collection as its source.

Un stream peut être parcours avec un foreach qui permet d’appliquer une fonction sur chaque élément au fur et à mesure de leur production.

personnes.stream().forEach(System.out::println);

D’autre classe produisent des Stream comme les fichiers. Noter le getResource pour obtenir le chemin absolu.

String filename = this.getClass().class.getResource("/test.txt").getFile();
try (FileReader fileReader = new FileReader(filename);
  BufferedReader br = new BufferedReader(fileReader)) {
  br.lines().forEach(System.out::println);
}
try (Stream stream = Files.lines(Paths.get(filename))) {
            stream.forEach(System.out::println);
}

Il existe des versions plus performantes pour les primitifs IntStream, DoubleStream, et LongStream.

Un stream peut aussi être filtré.

personnes.stream().filter(p -> p.getAge() > 18).forEach(System.out::println);

Et il permet d’utiliser le modèle map/reduce.

double ageMoyen = personnes.parallelStream().mapToInt(Personne::getAge).average().getAsDouble();
System.out.println(ageMoyen);
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("nashorn");
 
engine.eval("function pers(p,n) { return {nom:n, prenom:p} }");
engine.eval("print(JSON.stringify(pers('Pierre', 'Durand')))");

Objet qui peut contenir ou non une valeur null. Le but faciliter le traitement des null pointer exceptions.

—- dataentry page —- type : Howto technologie_tags : Java, Java8 theme_tags : POO