Thématiques principales

jeudi 2 novembre 2017

Evolution Java 8

Nous voila arrivé a la dernière version officielle de Java... Enfin officiel, en fait non car Java 9 vient de sortir mais avant qu'elle ne devienne la référence, Java 8 a encore quelques mois devant elle.
Voyons ses évolutions principales et moins principales.

L’implémentation par défaut des interfaces, les defender methods

Cette feature n'est pas forcement celle que l'on doit utiliser, a priori, celle ci est surtout l'objet d'un besoin de compatibilité des API avec d ancienne version de celle-ci en fournissant des comportements sans avoir a les implémentés.

1
2
3
4
5
public interface InterfaceAMoi {
    default void test(){
        System.out.println("a implementer");
    };
}

Bon ils est vrai que sur le fond c'est probablement l'une des grosses surprises de Java 8, car une interface ne définie qu'une interface de réalisation, voici que celle ci va fournir une comportement par défaut. Alors bien sur une interface n'est pas une classe et ne peut donc que manipuler par défaut que le comportement mais on peut se demander si nous ne risquons pas de retomber dans les problèmes du c++ et des héritages multiples que permettait d’éviter les anciennes interfaces. Il va falloir rester très prudent avec cette nouvelle feature de Java. L'exemple de ce probleme est tres bien expliqué dans cet article.

En parlant d'interface, on peut noter que l'on peut maintenant y définir des méthodes statique. Cela est plutôt mineure comme avancé mais cela va permettre de ne plus avoir a construire des classes a constructeur privé juste pour définir des bibliothèques de fonctions utilitaires.

Lambda expressions

Les lambda expressions sont la grosse evolution de Java 8. Issues des approches fonctionnelles, elles permettent la définition a la volée d'une fonction directement utilisable. Son intérêt et globalement la simplification du code qui jusque la devait être écrit sous la forme de classe interne anonyme comme on pouvait le faire avec Swing.

Donc avant on écrivait ceci:

1
2
3
4
5
6
7
public static void main(String[] args) {
  Runnable c=new Runnable(){
    @Override
    public void run() {
      System.out.println("J'implemente ma classe annonyme");
    }};
}

Désormais on écrira ceci:

1
2
3
4
5
public static void main(String[] args) {
  Runnable c=()->{
     System.out.println("J'implemente ma lambda expression");
  };
}

Bien sur il existe des contraintes. La première est qu'une lambda expression se construit sur la base d'une interface (pas d'une classe abstraite) ne comportant qu'une seule méthode. Voila pourquoi on peut utiliser les lambda expression a partir de l'interface Runnable.

Donc si on définit une interface :

1
2
3
public interface Toto {
    public void toto();
}

et on l'utilise comme suit

1
2
3
Toto c=()->{
      System.out.println("J'implemente ma lambda expression");
 };

A noter que l'utilisation des classes anonymes n'a jamais été une bonne pratique, on peut donc se demander si les lambda expression en sont une puisque finalement elles ne sont qu'un sucre syntaxique permettant de simplifier l’écriture et la lecture du code (pour ceux qui ont l'habitude de les manipuler....)

A mon sens, avec ce que l'on vient seulement de découvrir, les lambda expression ouvrent la possibilité d'une utilisation plus souple et plus dynamique de comportement en facilitant l'emploi de certains pattern de conception comme les patterns décorateurstratégie, médiateur ou encore de type fabrique ou commande. Mais pour comprendre tout cela il faudrait regarder plus profondément  dans package java.util.function.

java.util.function

Ce package contient un grand nombre d'interfaces fonctionnelles mais 3 principales sont a citer car participant de façon de façon générales au sein de tous types d'approches fonctionnelles:

  • les predicats pour tester les functions
  • les consumers pour appliquer des fonctions
  • les opérateurs pour combiner des opérations et des éléments

A noter pour les logiciens que ces concepts modélisent de façon générale des logiques de tous types (mais je reviendrais sur ces points dans des articles dédiés aux logiques)

Les références méthodes

Elles fonctionnent comme des lambda expression mais permettent de simplifier l’implémentation d'une classe anonyme. In fine l'utilisation de ce mécanisme permet d'appeler une méthode de classe ou d'instance de façon groupée en réalisant une lambda expression de façon implicite

au lieu de:

1
2
3
4
5
6
7
List<String> l = new ArrayList<>();
l.sort(new Comparator<String>() {
  @Override
  public int compare(String o1, String o2) {
    return o1.compareTo(o2);
  }
});

on aura:

1
2
List<String> l = new ArrayList<>();
l.sort(String::compareTo);

Ce qui indéniablement simplifie l’écriture lorsque l’implémentation elle même est simple....et commutative le cas échéant....

A noter en digression sur ce chapitre que java 8 offre maintenant la capacité de tri parrallele sur les tableaux:

1
Arrays.parallelSort(myArray);

Dans d'autres cas ca sera plus simple:


1
l.forEach(String::trim);

Stream methode

Les streams permettent le traitement de listes sous la forme d'un flux un peu comme le ferai un iterator mais orienté fonctionnelle. Ainsi naturellement, les lambda expressions vont s'appliquer dessus au travers de divers activités telles que les filtres.

Les streams vont fournir également des capacités sur le processus de traitement en permettant soit de séquentialisé les opérations (par exemple si l'ordre des éléments a une importance) ou d’être parallélisé si l'ordre n'a pas d'importance et que les données peuvent être partitionnées.

Par exemple pour récupérer la sous-liste des chaines démarrant par S dans une liste de chaînes:


1
2
List<String> l = new ArrayList<>();
List<String> filtredl=l.stream().filter((s)-> s.startsWith("S")).collect(Collectors.toList());

L'incorporation de Nashorn

Apres l'ajout d'une API de scripting et d'un interpreteur javascript (Rhino) dans Java 6, il aura fallu attendre Java 8 et l'integration de Nashorn pour beneficier d'un moteur JavaScript du type de NodeJS. L'article de Soat en donne une bonne description.

Time API

Java 8 intègre une refonte complète de l'API gérant le temps car celle ci manquait de cohérence générale. Ainsi avec cette mise a jour, Java 8 propose une API fournissant un ensemble de classes plus rationnelles, thread-safe et immutable.

rationalisant les besoins généraux de la gestion du temps et des dates en les . Je n'entre pas dans le détail car l'article suivant fourni déjà tout les exemples nécessaires a une bonne compréhension et si voulez plus d'info, cet article ci vous conviendra peut être mieux.

Les annotations

Dans la liste des améliorations on notera la possibilité de placer plusieurs fois sur le même éléments une même annotation et également de pouvoir en placer sur les types. Ceci aura pour intérêt, en donnant au compilateur les framework de vérification adéquat, d'ajouter des contraintes sur le code afin d'en garantir une meilleur qualité.

Java IO

On notera également quelques plus sur l'API IO introduite avec Java 7 pour une manipulation des fichiers en cohérence avec l'introduction des streams.

Les verrous  stamped

Après les verrous de lecture ecriture venus avec la Java 7, Java 8 introduit les verrous stamped. Autant si les premiers sont d'une utilisation tres simple, ils soufrent de problème de performances, ainsi, les seconds seront a optimiser afin de satisfaire aux éventuels problème de concurrence. Par ce fait, ils sont par contre plus compliqué a utiliser.

On entrera dans les details de la programmation multi-thread dans un autre article. Cependant je vous invite a lire celui-ci si vous souhaitez avancer sur le sujet.

Les concurrent Adders

Il sont la en remplacement des Atomic afin de fournir une solution de comptage concurrente et non bloquante. Tout comme les verrous en lecture ecriture, les Atomic on été intégrés dans Java 7 pour simplifier l’écriture et l’emploie de bloc synchronised sur de simple compteur (voir article) Seulement, comportant certaines limitations (de contention essentiellement), Java 8 apporte une nouvelle solution les Adders.

Process

 La classe Process se voit améliorée de trois méthodes pour vérifier l’état de vie d'un processus système, pour le détruire ou pour l'attendre. Ces méthodes complètent celles existantes de l'API JNI qui permettait de lancer des processus au sein de l'OS hebergeant la JVM.

SecureRandom

Java 8 soufrait d'un manque de securité dans la génération des nombres aléatoires pour les clefs de chiffrements. Elle offre désormais une API  stable quelque soit l'OS.

Optional

 L'api Optional est une nouvelle évolution de Java 8 dont le but est de pousser le développeur a ne plus utiliser le mots clef null comme d'un type. En effet, dans de nombreuses méthodes, il arrivait qu' il soit nécessaire de finaliser celle ci par un return null qui oblige nécessairement l'utilisateur de la méthode a préalablement vérifier la non nullité des données.

Avec Optional, on dispose d'une API permettant d’éviter de fournir un null voir de fournir une valeur par défaut adapté au type réel retourné de la méthode.

L'article suivant fournit l'ensemble des cas d'utilisation de cette classe et cet article ci décrit la nature de ce qu'est un Optional.

Attention a l'abus, Optional ne sert qu'a éviter de faire un retour de méthode null et ne s'utilise pas comme d'un Mock.... Optional ne sert qu'a contourner la nullité.

Suppression du PermGen

Avant de parler de Java 8 je vous propose de consulter le fonctionnement de la gestion memoire avant java 8 et celui ci sur l'optimisation.

Dans Java8, les développeurs vont voir disparaitre la PermGen ou permanent generation. Cet espace mémoire dédié au chargement des classes. Disparition? non quand même pas en fait, la PermGen a etre renommé en Metaspace qui va trouver sa place dans la mémoire native. En fait, depuis que la JVM est capable de charger et décharger des Classloader personnalisé, on va désormais trouver un Metaspace par Classloader permettant de charger et décharger directement dans la mémoire des classes. A noter que implémentation de Classloader personnalisé peut etre fastidieux et je recommande grandement l'utilisation d'OSGI. Mais ceci est une autre histoire.... enfin article.

Pour plus de détails n’hésitez pas a consulter cet article.

Voila, l'article sur la conséquente version Java 8 est terminer, Elle aura ete longue et pourtant je n'ai pu entrer dans trop de détails.... je tacherai de fournir quelques articles supplémentaires afin de compléter celui-ci.

Edit (06/11/2019)

Une référence utile sur la concaténation de chaînes :
https://redfin.engineering/java-string-concatenation-which-way-is-best-8f590a7d22a8

Aucun commentaire:

Enregistrer un commentaire