Java 8
FIP ING 39
Serge Rosmorduc
[email protected]
Conservatoire National des Arts et Métiers
2016-2021
Serge Rosmorduc Java 8 2016-2017 1 / 28
But du cours
montrer la grande nouveauté de java 8 : les lambda-expressions ;
compléter par quelques autres nouveautés qui améliorent la vie.
Serge Rosmorduc Java 8 2016-2017 2 / 28
Méthodes par défaut dans les interfaces
une interface ne contient que des en-têtes de méthodes ;
souvent, il y a cependant une certaine redondance entre celles-ci :
1 /∗ i n t e r f a c e p o u r l e s t r u c s q u i r e s s e m b l e n t
2 à d e s t a b l e a u x ∗/
3 p u b l i c i n t e r f a c e I n d e x a b l e <T> {
4 int size ();
5
6 T get ( int i ) ;
7
8 void s e t ( i n t i , T v a l ) ;
9
10 void i n v e r s e r ( ) ;
11 }
Ici, il serait possible d’écrire la méthode inverser sans faire référence à
une implémentation.
Serge Rosmorduc Java 8 2016-2017 3 / 28
Solution usuelle en java 7
On crée une classe abstraite pour ça :
1 p u b l i c a b s t r a c t c l a s s A b s t r a c t I n d e x a b l e <T>
2 implements I n d e x a b l e <T>{
3 public void i n v e r s e r ( ) {
4 i n t i= 0 ; i n t j= s i z e ( ) −1 ;
5 while ( i < j ) {
6 T tmp= g e t ( i ) ; s e t ( i , g e t ( j ) ) ;
7 s e t ( j , tmp ) ;
8 i ++; j −−;
9 }
0 }
1 }
les classes concrètes peuvent alors, au choix
implémenter toutes les méthodes d’Indexable ;
étendre AbstractIndexable et profiter de l’implémentation
d’inverser.
Serge Rosmorduc Java 8 2016-2017 4 / 28
Méthodes par défaut
Pour diminuer cette nécessité d’utilisation d’une classe abstraite, java 8
introduit la notion de « méthode par défaut » dans une interface.
une interface peut contenir le corps d’une méthode (précédé de
default) ;
elle ne peut toujours pas contenir de variable d’instance (sinon, ce
serait une classe) ;
quand on implémente une telle interface, on n’est pas obligé de
réécrire les méthodes par défaut.
une classe peut implémenter plusieurs interfaces avec des méthodes
par défaut
s’il y a conflit (deux méthodes par défaut de même signature sont
héritées), elle doit les redéfinir ou c’est une erreur.
Serge Rosmorduc Java 8 2016-2017 5 / 28
Méthodes par défaut
1 p u b l i c i n t e r f a c e I n d e x a b l e <T> {
2 int size ();
3
4 T get ( int i ) ;
5
6 void s e t ( i n t i , T v a l ) ;
7
8 default void i n v e r s e r ( ) {
9 i n t i= 0 ; i n t j= s i z e ( ) −1 ;
0 while ( i < j ) {
1 T tmp= g e t ( i ) ;
2 set ( i , get ( j ) ) ;
3 s e t ( j , tmp ) ;
4 i ++;
5 j −−;
6 }
7 }
8 }
Serge Rosmorduc Java 8 2016-2017 6 / 28
Les fonctions comme données
1 c l a s s F i l t r e J a v a implements F i l e n a m e F i l t e r {
2 @Override
3 p u b l i c boolean a c c e p t ( F i l e d i r , S t r i n g name ) {
4 r e t u r n name . endsWith ( " . j a v a " ) ;
5 }
6 }
7
8 public class ListerJava {
9 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
0 F i l e d= new F i l e ( " s r c / main / j a v a /demo/ i n t r o f o n c t i o n s " ) ;
1 f o r ( F i l e f : d.listFiles(new FiltreJava()) ) {
2 System . o u t . p r i n t l n ( f . g e t A b s o l u t e P a t h ( ) ) ;
3 }
4 }
5 }
FiltreJava sert uniquement à « enrober » la fonction accept qui fait
return name.endsWith(".java")
long et souvent peu lisible.
Serge Rosmorduc Java 8 2016-2017 7 / 28
Autre exemple
1 c l a s s MaTache implements A c t i o n L i s t e n e r {
2 @Override
3 public void actionPerformed ( ActionEvent e ) {
4 System . o u t . p r i n t l n ( "On␣ t r a v a i l l e ␣ ! ! " ) ;
5 }
6 }
7 p u b l i c c l a s s DemoTimer {
8 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
9 Timer t i m e r= new Timer ( 1 0 0 0 , new MaTache ( ) ) ;
0 timer . s t a r t ( ) ;
1 }
2 }
Serge Rosmorduc Java 8 2016-2017 8 / 28
Les fonctions comme données
1 c l a s s F i l t r e J a v a implements F i l e n a m e F i l t e r {
2 @Override
3 p u b l i c boolean a c c e p t ( F i l e d i r , S t r i n g name ) {
4 r e t u r n name . endsWith ( " . j a v a " ) ;
5 }
6 }
7
8 public class ListerJava {
9 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
0 F i l e d= new F i l e ( " s r c / main / j a v a /demo/ i n t r o f o n c t i o n s " ) ;
1 f o r ( F i l e f : d.listFiles(new FiltreJava()) ) {
2 System . o u t . p r i n t l n ( f . g e t A b s o l u t e P a t h ( ) ) ;
3 }
4 }
5 }
Serge Rosmorduc Java 8 2016-2017 9 / 28
Le même en java 8 ! ! !
1 public class ListerJava2 {
2 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
3 F i l e d i r= new F i l e ( " s r c / main / j a v a /demo/ i n t r o f o n c t i o n s " ) ;
4 for ( F i l e f :
5 d i r . l i s t F i l e s ( (d,n) -> n.endsWith(".java") ) ) {
6 System . o u t . p r i n t l n ( f . g e t A b s o l u t e P a t h ( ) ) ;
7 }
8 }
9 }
(d,n) -> n.endsWith(".java") définit à la volée la fonction
accept ;
c’est une lambda expression.
Serge Rosmorduc Java 8 2016-2017 10 / 28
Une page de pub : les langages fonctionnels
lisp, scheme, scala, clojure, ocaml, haskell, ruby ? python ?
depuis 1958 ;
gros regain d’intérêt à cause du parallélisme.
Serge Rosmorduc Java 8 2016-2017 11 / 28
Quel est le truc ?
sucre syntaxique : on a toujours besoin d’une classe et d’un objet de
cette classe ;
mais le compilateur les crée à la volée à votre place ;
création d’une classe interne anonyme (on en reparle plus tard,
promis)
pas forcément de déclaration : inférence de types ;
une lambda peut remplacer toute valeur dont le type est une interface
fonctionnelle.
Serge Rosmorduc Java 8 2016-2017 12 / 28
Interface fonctionnelle
l’idée : une interface qui définit une et une seule fonction.
1 public interface FilenameFilter {
2 boolean a c c e p t ( F i l e d i r , S t r i n g name ) ;
3 }
On peut écrire :
1 FilenameFilter f i l t e r=
2 ( F i l e d , S t r i n g n ) −> n . endsWith ( " . c " ) ;
inférence de type : si on a
1 FilenameFilter f i l t e r=
2 ( d , n ) −> n . endsWith ( " . c " ) ;
On sait que d est de type File et n de type String.
définition : interface qui a exactement une méthode abstraite ;
elle peut avoir une ou plusieurs méthodes par défaut.
Serge Rosmorduc Java 8 2016-2017 13 / 28
Exemple
1 @FunctionalInterface
2 public interface FonctionReelle {
3 double e v a l ( double x ) ;
4
5 d e f a u l t double d e r i v e e ( double x , double d e l t a ) {
6 r e t u r n ( e v a l ( x+d e l t a ) − e v a l ( x−d e l t a ) ) / ( 2 ∗ d e l t a ) ;
7 }
8 }
Serge Rosmorduc Java 8 2016-2017 14 / 28
Lambda et collections : le filtre
idée : ne conserver que les éléments d’une collection qui vérifient un filtre
booléen.
Exemple : ne conserver que les messages de Toto :
1 L i s t <Message> r e s= new A r r a y L i s t < >();
2 f o r ( Message m: l e s M e s s a g e s ) {
3 i f (m. g e t A u t e u r ( ) . e q u a l s ( " t o t o " ) )
4 r e s . add (m) ;
5 }
type de traitement très courant... du coup :
1 L i s t <Message> r= l e s M e s s a g e s . s t r e a m ( )
2 . f i l t e r (m −> m. g e t A u t e u r ( ) . e q u a l s ( " t o t o " ) )
3 . collect ( Collectors . toList ());
Serge Rosmorduc Java 8 2016-2017 15 / 28
Lambda et collections : les streams
vue d’une collection comme une suite d’éléments auquels on applique
des fonctions ;
créés par la méthode stream()
parfois parallélisables : méthode parallelStream() de Collection ;
Serge Rosmorduc Java 8 2016-2017 16 / 28
Petite démo parallèle
1 p u b l i c c l a s s DemoTri {
2 p r i v a t e s t a t i c void time ( Runnable r ) {
3 long s t a r t = System . nanoTime ( ) ;
4 r . run ( ) ;
5 long end = System . nanoTime ( ) ;
6 System . o u t . p r i n t l n ( " Temps␣ mis ␣ " + ( end − s t a r t ) / 1 . 0 e9 )
7 }
8
9 p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {
0 A r r a y L i s t <Double> l = new A r r a y L i s t < >();
1 f o r ( i n t i = 0 ; i < 1 _000_000 ; i ++) {
2 l . add ( Math . random ( ) ) ;
3 }
4 t i m e ( ( ) −> l . s t r e a m ( ) . s o r t e d ( ) . t o A r r a y ( ) ) ;
5 t i m e ( ( ) −> l . p a r a l l e l S t r e a m ( ) . s o r t e d ( ) . t o A r r a y ( ) ) ;
6 t i m e ( ( ) −> l . s t r e a m ( ) . s o r t e d ( ) . t o A r r a y ( ) ) ;
7 t i m e ( ( ) −> l . p a r a l l e l S t r e a m ( ) . s o r t e d ( ) . t o A r r a y ( ) ) ;
8 }
9 }
(note Serge
: premier
Rosmorducrun pas très juste) Java 8 2016-2017 17 / 28
collect
permet de « revenir » des streams aux collections (ou à d’autres types).
Rassemble les résultats d’un traitement à l’aide d’un Collector
En pratique, on utilise les collectors définis dans la classe
Collectors :
toList()
toSet()
counting
groupingBy
Collectors.joining : concaténation
...
Serge Rosmorduc Java 8 2016-2017 18 / 28
map
applique une fonction à tous les éléments du stream, et renvoie un autre
stream :
1 L i s t <S t r i n g > t i t r e s =
2 messages . stream ()
3 . map (m −> m. g e t T i t l e ( ) )
4 . collect ( Collectors . toList ());
Serge Rosmorduc Java 8 2016-2017 19 / 28
Combinaison des opérations
1 Set<S t r i n g > t i t r e s B y T o t o=
2 messages . stream ()
3 . f i l t e r (m−> m. g e t A u t h o r s ( ) . e q u a l s ( " t o t o " ) )
4 . map (m −> m. g e t T i t l e ( ) )
5 . c o l l e c t ( C o l l e c t o r s . toSet ( ) ) ;
Serge Rosmorduc Java 8 2016-2017 20 / 28
reduce
alternative à collect ;
permet de combiner les éléments du stream en un seul à l’aide d’un
opérateur ;
soit T le type des éléments du stream, et une opération f :
T ×T →T ;
alors reduce applique cette opération à tous les éléments du stream.
Exemple
1 L i s t <I n t e g e r > l= A r r a y s . a s L i s t ( 3 , 1 0 , 7 , 1 5 , 2 0 ) ;
2 System . o u t . p r i n t l n ( l . s t r e a m ( ) . r e d u c e ( 1 , ( a , b)−> a ∗b ) ) ;
Serge Rosmorduc Java 8 2016-2017 21 / 28
Pointeurs de méthodes
Il arrive très souvent de vouloir définir une lambda qui soit :
ou une méthode statique d’une classe :
1 A r r a y L i s t <S t r i n g > l= . . . ;
2 L i s t <S t r i n g > l 1= l . s t r e a m ( )
3 . map ( s −> S t r i n g U t i l s . i n v e r s e r ( s ) )
4 . collect ( Collectors . toList ());
ou une méthode des objets contenus dans le Stream :
1 A r r a y L i s t <S t r i n g > l= . . . ;
2 L i s t <S t r i n g > l 1= l . s t r e a m ( )
3 . map ( s −> s . t o U p p e r c a s e ( ) )
4 . collect ( Collectors . toList ());
pour ces deux cas, on dispose d’une notation allégée :
Serge Rosmorduc Java 8 2016-2017 22 / 28
Pointeurs de méthodes
Il arrive très souvent de vouloir définir une lambda qui soit :
ou une méthode statique d’une classe :
1 A r r a y L i s t <S t r i n g > l= . . . ;
2 L i s t <S t r i n g > l 1= l . s t r e a m ( )
3 . map ( StringUtils::inverser )
4 . collect ( Collectors . toList ());
ou une méthode des objets contenus dans le Stream :
1 A r r a y L i s t <S t r i n g > l= . . . ;
2 L i s t <S t r i n g > l 1= l . s t r e a m ( )
3 . map ( String::toUppercase )
4 . collect ( Collectors . toList ());
Serge Rosmorduc Java 8 2016-2017 23 / 28
Tony Hoare’s « Null References : The Billion Dollar
Mistake »
À propos de null :
I call it my billion-dollar mistake. It was the invention of the null
reference in 1965. At that time, I was designing the first compre-
hensive type system for references in an object oriented language
(ALGOL W). My goal was to ensure that all use of references
should be absolutely safe, with checking performed automatically
by the compiler. But I couldn’t resist the temptation to put in a
null reference, simply because it was so easy to implement. This
has led to innumerable errors, vulnerabilities, and system crashes,
which have probably caused a billion dollars of pain and damage
in the last forty years.
Serge Rosmorduc Java 8 2016-2017 24 / 28
Optional
une méthode retourne une valeur ou reçoit un argument ;
dans certains cas, on veut dire que cette valeur peut être absente ;
solution usuelle : renvoyer (ou passer) null.
Problème
Ça n’est pas explicite. Du coup, le programmeur se méfie de tout
argument ou toute valeur retournée de type objet.
Optional permet de le rendre explicite ;
ça ne résoud que partiellement le problème (il est au niveau du
langage lui-même) ;
Serge Rosmorduc Java 8 2016-2017 25 / 28
Optional
Construction
1 O p t i o n a l <I n t e g e r > a= O p t i o n a l . o f ( 3 ) ;
2 O p t i o n a l <I n t e g e r > b= O p t i o n a l . empty ( ) ;
méthodes
isPresent() : renvoie vrai si on a une valeur ;
filter/map : comme pour un stream ;
orElse(autreValeur) : la valeur (si elle est là), sinon une valeur par
défaut ;
ifPresent(Consumer consumer) : prend comme argument une fonction
qui fera quelque chose avec notre élément.
get() : renvoie la valeur (ou lève une exception).
Serge Rosmorduc Java 8 2016-2017 26 / 28
Interfaces générales
Pour faciliter la vie, java définit un certain nombre d’interfaces très
générales dans le package java.util.function :
Function<T,R> : une fonction qui prend un argument T et retourne
R;
DoubleUnaryOperator : prend un double et retourne un double ;
Consumer<R> : prend comme argument un objet de type R et
retourne void.
...
Serge Rosmorduc Java 8 2016-2017 27 / 28
λ et Comparator
En java 8, l’interface Comparator est dotée de méthodes statiques utiles
pour créer des comparateurs à la volée :
1 L i s t <P e r s o n n e > l= . . . ;
2 Collections . sort ( l ,
3 Comparator
4 . c o m p a r i n g ( P e r s o n n e : : getNom )
5 . t h e n C o m p a r i n g I n t ( P e r s o n n e : : getAge ) ) ;
Serge Rosmorduc Java 8 2016-2017 28 / 28