Vous êtes sur la page 1sur 50

Sommaire nouveautés Java

• Programmation générique (à partir de la version 1.5)


• Les classes Anonymes
• Interfaces fonctionnelles
• Méthode par défaut et statiques dans les interfaces
• Les expressions lambda (à partir de la version 1.8) et Reference de méthode
• L’API Stream
• Nouvel API de Date and Time
• Java FX
• Annotation de type
• package java.nio.file (java 7)
• Gestion de la concurrence : ThreadSafe, Synchronisation, nouveautés de java 8, …
• La réflexion
• Quelques notions de la JVM : gestion de la mémoire, …
Programmation générique (depuis java 5)

• Principe de base
Nous allons coder une classe Solo. Celle-ci va travailler avec des références de type String. Voici le
diagramme de classe de cette dernière en figure suivante.

La classe Solo permet d’affecter une valeur, la mettre à jour et la récupérer… Maintenant, si je vous
demande de me faire une classe qui permet de travailler avec n'importe quel type de données
Programmation générique

• Principe de base (suite 1)


Une proposition public class Test {
public static void main(String[] args) {
Solo val = new Solo(12);
int nbre = val.getValeur();
}
}

Cast obligatoire

Solo val = new Solo(12);


int nbre = (Integer)val.getValeur();

Vous serez donc sans doute tentés d'écrire une classe par type de donnée (SoloInt, SoloString ,
etc.). Et c'est là que la généricité s'avère utile, car avec cette dernière, vous pourrez savoir ce que
contient votre objet Solo et n'aurez qu'une seule classe à développer
Programmation générique
public class Solo<T> {
• Principe de base (suite 2)
//Variable d'instance
Objet générique private T valeur;

//Constructeur par défaut


public Solo(){
this.valeur = null;
}

//Constructeur avec paramètre inconnu pour l'instant


public Solo(T val){
this.valeur = val;
Dans cette classe, le T n'est pas encore défini. }
Vous vous en occuperez à l'instanciation de la
classe. //Définit la valeur avec le paramètre
Exemple d’utilisation public void setValeur(T val){
this.valeur = val;
}

public static void main(String[] args) { //Retourne la valeur déjà « castée » par la signature de la méthode !
Solo<Integer> val = new Solo<Integer>(12); public T getValeur(){
int nbre = val.getValeur(); return this.valeur;
} }
}
Programmation générique
public class Duo<T, S> {
//Variable d'instance de type T
private T valeur1;
• Principe de base (suite 3) //Variable d'instance de type S
La généricité peut être multiple private S valeur2;

//Constructeur par défaut


public Duo(){
this.valeur1 = null;
public static void main(String[] args) { this.valeur2 = null;
Duo<String, Boolean> dual = new Duo<String, Boolean>("toto", true); }
System.out.println("Valeur de l'objet dual : val1 = " + dual.getValeur1() + ", val2 = " + dual.getValeur2());
//Constructeur avec paramètres
Duo<Double, Character> dual2 = new Duo<Double, Character>(12.2585, 'C'); public Duo(T val1, S val2){
System.out.println("Valeur de l'objet dual2 : val1 = " + dual2.getValeur1() + ", val2 = " + dual2.getValeur2()); this.valeur1 = val1;
} this.valeur2 = val2;
}

//Méthodes d'initialisation des deux valeurs


public void setValeur(T val1, S val2){
this.valeur1 = val1;
this.valeur2 = val2;
}
….
}
Les classes Anonymes (depuis java 7)
Exemple :
Une classe anonyme permet de modifier le comportement de soin de notre personnage

Accolades après
l’instanciation

Le nouveau soin n'est définie que pour cet objet. Nous devons seulement redéfinir la (ou les) méthode(s) de
l'interface ou de la classe abstraite dans un bloc d'instructions ; d'où les accolades après l'instanciation.
Les classes Anonymes
Utiliser une classe anonyme revient à créer une classe fille sans être obligé de créer cette classe de façon
explicite. L'héritage se produit automatiquement. La classe créée n'a pas de nom, l'héritage s'effectue de
façon implicite !
Nous bénéficions donc de tous les avantages de la classe mère en ne redéfinissant que la méthode qui
nous intéresse.

Les classes anonymes sont soumises aux mêmes règles que les classes « normales » :
• Utilisation des méthodes non redéfinies de la classe mère ;
• Obligation de redéfinir toutes les méthodes d'une interface ;
• Obligation de redéfinir les méthodes abstraites d'une classe abstraite.

Cependant, ces classes possèdent des restrictions à cause de leur rôle et de leur raison d'être :
• Elles ne peuvent pas être déclarées abstract ;
• Elles ne peuvent pas non plus être déclarées static ;
• Elles ne peuvent pas définir de constructeur ;
• Elles sont automatiquement déclarées final : on ne peut dériver de cette classe, l'héritage est donc
impossible !
Les interfaces et java 8
Java 8 permet aux interfaces de définir des méthodes avec un comportement par défaut et de définir des méthodes
statiques.
Méthodes statiques :
Une interface est une classe 100% abstraite, ce n'est plus le cas depuis Java 8 car cette interface est valide et ce code
fonctionne parfaitement :
Les interfaces et java 8
Le code de test ci-dessous donne le résultat
Méthodes statiques (suite): ci-après
Nous pouvons même rajouter un niveau d'interface
supplémentaire
Les interfaces et java 8
Méthode par défaut :
depuis Java 8, il est possible d'ajouter un comportement par
défaut à des méthodes dans une interface grâce au mot
clé default

Je suis un alien et :
Je ponds des oeufs !
Je me divise
Les interfaces et java 8
Méthode par défaut (suite):
Il est à présent possible de bénéficier d'un comportement par défaut dans des méthodes d'interfaces.
Ceci permet notamment d'assurer une compatibilité à vos interfaces si vous souhaitez rajouter des méthodes
dans celles-ci. En effet, dans un projet informatique de taille, vous aurez tout un tas d'interfaces et rajouter
une méthode dans une interface voulais dire, avant Java 8 et les méthodes par défaut, repasser sur toutes les
classes qui implémentent l'interface afin de redéfinir la nouvelle méthode : ce n'est plus la peine avec java 8.

En résumé
• Avec Java 8, une interface n'est plus une classe 100% abstraite.
• Elle peut contenir des méthodes concrètes sous deux formes :
• Avec des méthodes statiques;
• Avec une définition par défaut d'une méthode.
Les interfaces fonctionnelles (depuis java 8)

Ce concept permet de définir une interface n'ayant qu'une et une seule méthode abstraite

Dans notre exemple précédent, notre interface Soin est une interface fonctionnelle car elle n'a qu'une
méthode à redéfinir (Single Abstract Method, ou interface SAM).
Ce type d'interface sera le pilier de l'utilisation des lambdas

Pour s'assurer que le contrat est bien respecté, Java propose d'annoter l'interface
avec @FunctionalInterface , comme ceci :
Avec l annotation, Java vous
indiquera qu'il y a un
problème
Les interfaces fonctionnelles

Mais vous avez tout à fait le droit d'avoir une interface fonctionnelle avec des méthodes par défaut, comme
le montre l'image ci-dessous :

Java 8 vient avec de nombreuses interfaces fonctionnelles prédéfinies dans le package java.util.function dont
le détail est disponible à l'adresse suivante :
https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
Les Lambdas (depuis java 8)
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").

Dans d'autres langages par contre, il existe des références vers ce qu'on appelle des closures : des morceaux de
codes anonymes.

Jusqu'en Java 7, la seul façon d'avoir ce type de référence revenait a faire une classe anonyme comme vu
précédemment. Depuis Java 8, les closures existent et s'appellent les lambdas.

Pour simplifier au maximum, une lambda est la redéfinition d'une méthode d'une interface fonctionnelle sans
avoir à faire une classe anonyme, donc gain de ligne de code et de visibilité.

Ainsi, nous allons pouvoir rencontrer un nouvel opérateur : ->.


Gardez en mémoire qu'une lambda permet de redéfinir une méthode abstraite d'une interface fonctionnelle donc,
par conséquent, elle doit répondre à la signature de la méthode concernée.

Voici concrètement comment se construit une expression lambda :


- () -> faire une action;
- (param1, param2) -> faire une action;
- (param1, param2, param3) -> {traitements ; retourne une valeur;} ;
Les Lambdas
Exemples :

Notre lambda ne prend aucun paramètre et retourne tout le temps 1337.


Le mot clé return ainsi que les accolades '{ }' sont facultatif pour une lambda avec une
seule instruction. Par contre, il faut toujours terminer un lambda par un ';'

Ici, la lambda est presque identique à la précédente à


l'exception près que là, nous affichons un message
dans la console avant de retourner un entier.

Notre lambda sera à utiliser pour une interface


fonctionnelle ayant une méthode abstraite attendant
deux paramètres et retournant un type numérique
Les Lambdas
Exemples (suite) :
Implémentation en utilisant les classes anonymes

Soit l interface fonctionnelle suivante :

Le même code avec une lambda


Le package java.util.function (depuis java 8)
Ce package embarque une quarantaine d'interfaces fonctionnelles qui peuvent répondre à vos besoins sans
que soyez obligé de faire votre propre interface notamment car celui-ci tire un grand avantage des génériques.
Exemples :
Le package java.util.function

Il existe des dérivées de ces interfaces qui spécifient un peut plus leur fonctionnement.
Par exemple il existe une interface IntFunction dont la signature de la méthode apply est la suivante :
R apply(int value). L'interface renverra un type générique mais prendra un entier en paramètre.

Il existe un bon nombre de ces interfaces et, en général, elles sont présentent pour chaque type mentionné sur
le slide précèdent.
En voici une liste non exhaustive IntFunction, IntSupplier, IntBinaryOperation,
IntConsumer, IntToDoubleConsumer, IntToDoubleFunction, …
Et ce code nous donne le résultat suivant :
Le package java.util.function : java.util.function.Function<T,R>
[toto, titi, tata, tutu]
[20, 40, 60, 80]
Le package java.util.function : java.util.function.Function<T,R> (suite)
Il est aussi possible de surcharger une méthode par défaut de ces interfaces fonctionnelles. Par exemple,
dans celle que nous venons d'utiliser, il y a la méthode addThen qui permet d'appliquer une fonction
après le traitement. Par exemple, nous obtenons exactement le même résultat que précédemment avec ce
code :
Le package java.util.function : java.util.function.Consumer<T>
Encore plus simple que la précédente interface car celle-ci ne retourne rien, elle se contente de "consommer"
un objet, donc d'y appliquer un traitement, comme par exemple ajouter 13 ans à l'age d'un objet Personne.

Ce qui nous donne :


Le package java.util.function : java.util.function.Predicate<T>
Le code ci-dessous permet de vérifier que l'objet Personne a un âge supérieur à 20 :

Ce qui nous donne :


tata a l'âge requis !
tutu a l'âge requis !
Les références de méthodes
Une référence de méthode sert a définir une méthode abstraite d'une interface fonctionnelle.
Cette magie opère grâce à un nouvel opérateur « :: ».

La méthode qui va être implémenter dans l'interface fonctionnelle par ce biais devra
correspondre à la signature de la méthode abstraite.

Pour simplifier au maximum, une référence de méthode est une lambda ultra simplifiée en
utilisant ni plus ni moins qu'une méthode déjà existante dans une interface (méthode
statique) classe (méthode statique ou constructeur) ou encore une méthode d'une instance
de classe. Voici comment la syntaxe est faite :
« classe, interface ou instance » :: « Nom de la méthode »

Voir les exemples ci après (nous en profiterons pour voir d'autres interfaces fonctionnelles
présentes dans Java 8)
Les références de méthodes (suite)
Le code ci-après nous fournit la sortie suivante :
0.1234567
0.1234567
Bonjour !
Bonjour !
New Integer created : class java.lang.Integer
L’API Stream de java 8
Le concept de Stream existe déjà depuis longtemps dans l'API I/O, notamment avec les interfaces InputStream et
OutputStream.
Il ne faut pas confondre l'API Stream de Java 8 avec les classes de type xxxStream de Java I/O.
Les streams de Java I/O permettent de lire ou écrire des données dans un flux (sockets, fichiers, ...). Le concept de Stream
de Java 8 est différent du concept de flux (stream) de l'API I/O même si l'approche de base est similaire : manipuler un flux
d'octets ou de caractères pour l'API I/O et manipuler un flux de données pour l'API Stream.
Cette dernière repose sur le concept de flux (stream en anglais) qui est une séquence d'éléments.
L'API Stream facilite l'exécution de traitements sur des données de manière séquentielle ou parallèle.
Les Streams permettent de laisser le développeur se concentrer sur les données et les traitements réalisés sur cet ensemble
de données sans avoir à se préoccuper de détails techniques de bas niveau comme l'itération sur chacun des éléments ou
la possibilité d'exécuter ces traitements de manière parallèle.
L'API Stream de Java 8 propose une approche fonctionnelle dans les développements avec Java. Elle permet de décrire de
manière concise et expressive un ensemble d'opérations dont le but est de réaliser des traitements sur les éléments d'une
source de données. Cette façon de faire est complètement différente de l'approche itérative utilisée dans les traitements
d'un ensemble de données avant Java 8.

Ceci permet au Stream de pouvoir :


- Optimiser les traitements exécutés grâce au lazyness et à l'utilisation d'opérations de type short-circuiting qui permettent
d'interrompre les traitements avant la fin si une condition est atteinte
- Exécuter certains traitements en parallèle à la demande du développeur
L’API Stream de java 8 (suite)
L'API Stream permet de réaliser des opérations fonctionnelles sur un ensemble d'éléments. De nombreuses
opérations de l'API Stream attendent en paramètre une interface fonctionnelle ce qui conduit naturellement à
utiliser les expressions lambdas et les références de méthodes dans la définition des Streams. Un Stream
permet donc d'exécuter des opérations standards dont les traitements sont exprimés grâce à des expressions
lambdas ou des références de méthodes.

Un Stream permet d'exécuter une agrégation d'opérations de manière séquentielle ou en parallèle sur une
séquence d'éléments obtenus à partir d'une source dans le but d'obtenir un résultat.

Le code utilisé est :


Concis et clair car il repose sur l'utilisation d'opérations prédéfinies Exprimé de manière déclarative (c'est une description
du résultat attendu) plutôt que de manière impérative (c'est une description des différentes étapes nécessaires à l'exécution
des traitements)
L’API Stream de java 8 (suite)
Les éléments traités par le Stream sont fournis par une source qui peut être de différents types :
- Une collection
- Un tableau
- Un flux I/O
- Une chaîne de caractères
...
Avec l'API Stream il est possible de déclarer de manière concise des traitements sur une source de données qui
seront exécutés de manière séquentielle ou parallèle.
Le traitement en parallèle d'un ensemble de données requiert plusieurs opérations :
- La séparation des données en différents paquets plus petits (forking)
- L'exécution dans un thread dédié des traitements sur chaque élément d'un paquet, généralement en utilisant
un pool de threads pour limiter la quantité de ressources utilisées
- La combinaison des résultats de chaque paquet pour obtenir le résultat final (joining)

Java 7 propose le framework Fork/Join pour faciliter la mise en œuvre de ces opérations en parallèle.
L'API Stream utilise ce framework pour l'exécution des traitements en parallèle.
L’API Stream de java 8 (suite)
L'utilisation de l'API Stream possède donc plusieurs avantages :
- L'exécution, sur un ensemble de données, de traitements définis de manière déclarative
- La quantité de code à produire pour obtenir un traitement similaire reposant sur sa propre itération est réduite
- La possibilité d'exécuter ses traitements en parallèle


L’API Stream de java 8 (suite)


L’API Stream de java 8 (suite)
parallelStream

Un Stream parallèle va en interne paralléliser l'exécution du pipeline d'opérations en


répartissant les données sur plusieurs threads.
L’API Stream de java 8 (suite)
Il existe deux types d'opérations : les opérations
intermédiaires et les opérations terminales. L'ensemble
des opérations effectuées par un Stream est appelé
pipeline d'opérations (operation pipeline).
La plupart des opérations d'un Stream attendent en
paramètre une interface fonctionnelle pour définir leur
comportement.
Ces paramètres peuvent ainsi être exprimés en utilisant
une expression lambda ou une référence de méthode.

Il est important que :


- Les opérations exécutées par le Stream ne modifient pas la source de données utilisée par le Stream
- Les données de la source de données ne doivent pas être modifiées durant leur traitement par le
Stream car cela pourrait avoir un impact sur leur exécution
L’API Stream de java 8 (suite) Les opérations intermédiaires ne
réalisent aucun traitement tant
que l'opération terminale n'est
invoquée.
Elles sont dites lazy ce qui
permettra éventuellement
d'effectuer des optimisations dans
leur exécution comme par
exemple de réaliser celle-ci dans
une même itération.
Optimisations sont mises en œuvre par le Stream :
- Tous les éléments ne sont pas parcourus
- L'opération de filtre n'est invoquée que 6 fois
- L'opération de transformation n'est invoquée que 3 fois
Ceci est possible car l'opération limit(3) ne renvoie qu'un
Stream qui contient au maximum le nombre d'éléments précisé
en paramètre.
Les opérations sont regroupées en une seule itération qui traite chaque élément en lui application le pipeline d'opérations :
les opérations n'itèrent pas individuellement sur les éléments du Stream.
Ceci permet d'optimiser au mieux les traitements à réaliser pour obtenir le résultat de manière efficiente.
L’API Stream de java 8 (suite)
Les opérations proposées par un Stream sont des opérations communes :
Pour filtrer des données, un Stream propose plusieurs opérations :
filter(Predicate) : renvoie un Stream pour lequel l'évaluation du Predicate passé en paramètre vaut true
distinct() : renvoie un Stream qui ne contient que les éléments uniques (elle retire les doublons).
La comparaison se fait grâce à l'implémentation de la méthode equals()
limit(n) : renvoie un Stream que ne contient comme éléments que le nombre fourni en paramètre
skip(n) : renvoie un Stream dont les n premiers éléments sont ignorés
Pour rechercher une correspondance avec des éléments, un Stream propose plusieurs opérations :
anyMatch(Predicate) : renvoie un booléen qui précise si l'évaluation du Predicate sur au moins un élément vaut true
allMatch(Predicate) : renvoie un booléen qui précise si l'évaluation du Predicate sur tous les éléments vaut true
noneMatch(Predicate) : renvoie un booléen qui précise si l'évaluation du Predicate sur tous les éléments vaut false
findAny() : renvoie un objet de type Optional qui encapsule un élément du Stream s'il existe
findFirst() : renvoie un objet de type Optional qui encapsule le premier élément du Stream s'il existe
Pour transformer des données, un Stream propose plusieurs opérations :
map(Function) : applique la Function fournie en paramètre pour transformer l'élément en créant un nouveau
flatMap(Function):applique la Function fournie en paramètre pour transformer l'élément en créant 0, 1 ou +eurs elts
Pour réduire les données et produire un résultat, un Stream propose plusieurs opérations :
reduce() : applique une Function pour combiner les éléments afin de produire le résultat
collect() : permet de transformer un Stream qui contiendra le résultat des traitements de réduction dans un
conteneur mutable
la nouvelle API de gestion des dates de Java 8
Java 8 introduced new APIs for Date and Time to address the shortcomings of the older java.util.Date and java.util.Calendar.
Let's start with the issues in the existing Date and Calendar APIs and discuss how the new Java 8 Date and Time APIs
address them.

We will also look at some of the core classes of the new Java 8 project that are part of the java.time package, such
as LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Period, Duration and their supported APIs (voir exemples).

Thread safety – The Date and Calendar classes are not thread safe, leaving developers to deal with the headache of hard-
to-debug concurrency issues and to write additional code to handle thread safety. On the contrary, the
new Date and Time APIs introduced in Java 8 are immutable and thread safe, thus taking that concurrency headache away
from developers.
API design and ease of understanding – The Date and Calendar APIs are poorly designed with inadequate methods to
perform day-to-day operations. The new Date/Time API is ISO-centric and follows consistent domain models for date, time,
duration and periods. There are a wide variety of utility methods that support the most common operations.
ZonedDate and Time – Developers had to write additional logic to handle time-zone logic with the old APIs, whereas with
the new APIs, handling of time zone can be done with Local and ZonedDate/Time APIs.
la nouvelle API de gestion des dates de Java 8
Exemples
la nouvelle API de gestion des dates de Java 8
Exemples
Java FX
• Bibliothèque Graphique
• Intégrée dans JRE et JDK
• Permet de réaliser des interfaces graphiques évoluées :
– Animations
– Effets
– 3D
– Audio
– Vidéo

• Une classe JavaFX doit hériter de la classe Application


(javafx.application.Application)
• Forcer l’utilisation d’un Thread JavaFX
Java FX
Main
Le point d'entrée pour les applications JavaFX est la classe Application. JavaFX exécute
dans l'ordre, chaque fois qu'une application est lancée, les actions suivantes :
- Construit une instance de la classe Application spécifiée
- Appelle la méthode init ()
- Appelle la méthode start (javafx.stage.Stage)
- Attend l'achèvement de l'application,
- Appelle la méthode stop ()

Notez que la méthode de démarrage (start) est abstraite et doit être surchargée. Les
méthodes d'initialisation (init) et d'arrêt (stop) ont des implémentations concrètes qui
ne font rien.
Java FX
Java FX
Java FX
Java FX
Java FX
Exemple
Java FX
Les évènements
Dans les applications JavaFX, les événements signifient que quelque chose s'est produit : un utilisateur a cliqué sur
un bouton, appuie sur une touche, déplace une souris ou effectue d'autres actions, …

Dans JavaFX, un événement est une instance de la classe javafx.event.Event ou de toute sous-classe d'Event.

JavaFX fournit plusieurs sous-classe d'Event, notamment DragEvent, KeyEvent, MouseEvent, ScrollEvent et
d'autres.
Java FX
Exemple complet
Java FX
Java FX vs Swing
JavaFX Cons:
- not quite as mature in terms of getting all the bugs out
- no "system" look and feel
- not truly built-in until Java 8  you must use java 8 or greatest
JavaFX Pros:
- cleaner API, much easier to work with
- CSS styling
- built-in animation system
- property system and bindings are great
- media support (e.g. video player)
- significant effort and investment from Oracle.
- Execution plus rapide (à mesurer )
Swing Cons:
- customizing look is more difficult (unless you are picking a pre-made look and feel)
- APIs are less consistent
- no built-in data-binding concept
Swing Pros:
- system look and feel (though some find it is not a close enough match, e.g. file chooser)
- stable and proven
Le framework Fork/Join de Java 7
Comprendre la réflexivité
JVM modulaire avec Java 9

Introduction sur la JVM


Ensuite JVM et java 9
Nouveautés de java 11

Vous aimerez peut-être aussi