JavaRush /Blog Java /Random-FR /Fonctionnalités Java 8 – Le guide ultime (partie 2)
0xFF
Niveau 9
Донецк

Fonctionnalités Java 8 – Le guide ultime (partie 2)

Publié dans le groupe Random-FR
La deuxième partie de la traduction de l'article Fonctionnalités Java 8 – Le guide ULTIMATE . La première partie est ici (le lien peut changer). Fonctionnalités Java 8 – Le guide ultime (Partie 2) - 1

5. Nouvelles fonctionnalités dans les bibliothèques Java 8

Java 8 a ajouté de nombreuses nouvelles classes et étendu celles existantes pour mieux prendre en charge la concurrence moderne, la programmation fonctionnelle, la date/heure, etc.

5.1. Cours facultatif

La célèbre NullPointerException est de loin la cause la plus fréquente des échecs des applications Java. Il y a longtemps, l'excellent projet Guava de Google était présenté Optionalcomme une solution NullPointerException, empêchant ainsi le code d'être pollué par des contrôles nuls, et encourageant ainsi l'écriture d'un code plus propre. La classe Guava, inspirée de Google, Optionalfait désormais partie de Java 8. OptionalCe n'est qu'un conteneur : elle peut contenir une valeur ou un type Т, ou simplement être nulle. Il fournit de nombreuses méthodes utiles pour que les vérifications nulles explicites ne soient plus justifiées. Reportez-vous à la documentation officielle pour des informations plus détaillées. Regardons deux petits exemples d'utilisation Optional: avec et sans null.
Optional<String> fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
La méthode isPresent()renvoie true si l'instance Optionalcontient une valeur non nulle et false sinon. La méthode orElseGet()contient un mécanisme de secours pour le résultat s'il Optionalcontient null, acceptant des fonctions pour générer une valeur par défaut. La méthode map () transforme la valeur actuelle Optionalet renvoie une nouvelle instance Optional. La méthode orElse()est similaire à orElseGet(), mais au lieu d'une fonction, elle prend une valeur par défaut. Voici le résultat de ce programme :
Full Name is set? false
Full Name: [none]
Hey Stranger!
Jetons un coup d'œil rapide à un autre exemple :
Optional<String> firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();
Le résultat sera comme ceci :
First Name is set? true
First Name: Tom
Hey Tom!
Pour des informations plus détaillées, veuillez vous référer à la documentation officielle .

5.2. Ruisseaux

La nouvelle API Stream ( java.util.stream) introduit une véritable programmation de style fonctionnel en Java. Il s'agit de loin de l'ajout le plus complet à la bibliothèque Java et permet aux développeurs Java d'être beaucoup plus efficaces et leur permet également de créer un code efficace, propre et concis. L'API Stream facilite grandement le traitement des collections (mais sans s'y limiter, comme nous le verrons plus tard). Prenons un exemple simple Task.
public class Streams  {
    private enum Status {
        OPEN, CLOSED
    };

    private static final class Task {
        private final Status status;
        private final Integer points;

        Task( final Status status, final Integer points ) {
            this.status = status;
            this.points = points;
        }

        public Integer getPoints() {
            return points;
        }

        public Status getStatus() {
            return status;
        }

        @Override
        public String toString() {
            return String.format( "[%s, %d]", status, points );
        }
    }
}
La tâche a une certaine idée des points (ou pseudo-difficultés) et peut être OUVERTE ou FERMÉE . Présentons une petite collection de problèmes avec lesquels jouer.
final Collection<Task> tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 )
);
La première question que nous avons l’intention de découvrir est la suivante : combien de points les tâches OPEN contiennent-elles actuellement ? Avant Java 8, la solution habituelle aurait été d'utiliser un itérateur foreach. Mais dans Java 8, la réponse réside dans les flux : une séquence d'éléments qui prennent en charge les opérations d'agrégation séquentielles et parallèles.
// Подсчет общего количества очков всех активных задач с использованием sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );
Et la sortie de la console ressemblera à :
Total points: 18
Regardons ce qui se passe ici. Tout d’abord, la collection de tâches est convertie en une représentation en continu. L'opération filterfiltre ensuite toutes les tâches ayant un statut FERMÉ . À l'étape suivante, l'opération mapToIntconvertit les stream Tasks en stream Integers en utilisant une méthode Task::getPointspour chaque instance Task. Enfin, tous les points sont additionnés à l'aide de la méthode sum, qui fournit le résultat final. Avant de passer aux exemples suivants, il y a quelques notes sur les fils de discussion à garder à l'esprit (plus de détails ici ). Les opérations streamsont divisées en opérations intermédiaires et finales . Les opérations intermédiaires renvoient un nouveau flux. Ils sont toujours paresseux ; lors de l'exécution d'opérations intermédiaires telles que filter, ils n'effectuent pas réellement de filtrage, mais créent à la place un nouveau flux qui, une fois terminé, contient les éléments du flux d'origine qui correspondent au prédicat donné. Les opérations finies , telles que forEachet sum, peuvent être transmises via un flux pour produire un résultat ou un effet secondaire. Une fois l’opération finale terminée, le flux est considéré comme utilisé et ne peut plus être utilisé. Dans presque tous les cas, les opérations finales ont tendance à terminer leur parcours à travers la source de données sous-jacente. Une autre fonctionnalité intéressante des threads est la prise en charge de processus parallèles prêts à l'emploi. Regardons cet exemple, qui trouve la somme des scores de tous les problèmes.
// Calculate total points of all tasks
final double totalPoints = tasks
   .stream()
   .parallel()
   .map( task -> task.getPoints() ) // or map( Task::getPoints )
   .reduce( 0, Integer::sum );

System.out.println( "Total points (all tasks): " + totalPoints );
Ceci est très similaire au premier exemple, sauf que nous essayons de traiter toutes les tâches en parallèle et de calculer le résultat final à l'aide de la méthode reduce. Voici le résultat de la console :
Total points (all tasks): 26.0
Il est souvent nécessaire de regrouper des éléments selon un certain critère. L'exemple montre comment les threads peuvent vous aider.
// Группировка задач по их статусу
final Map<Status, List<Task>> map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
La sortie de la console sera la suivante :
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
Pour terminer avec les exemples de problèmes, calculons le pourcentage global (ou poids) de chaque problème de la collection en fonction du total de points :
// Подсчет веса каждой задачи (How процент от общего количества очков)
final Collection<String> result = tasks
    .stream()                                        // Stream<String>
    .mapToInt( Task::getPoints )                     // IntStream
    .asLongStream()                                  // LongStream
    .mapToDouble( points -> points / totalPoints )   // DoubleStream
    .boxed()                                         // Stream<Double>
    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
    .mapToObj( percentage -> percentage + "%" )      // Stream<String>
    .collect( Collectors.toList() );                 // List<String>

System.out.println( result );
La sortie de la console ressemblera à ceci :
[19%, 50%, 30%]
Enfin, comme nous l'avons noté précédemment, l'API Stream n'est pas uniquement destinée aux collections Java. Une opération d'E/S typique, telle que la lecture de fichiers texte ligne par ligne, est un très bon candidat pour l'utilisation du traitement de flux. Voici un petit exemple pour le prouver.
final Path path = new File( filename ).toPath();
try( Stream<String> lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
La méthode onConsole, qui est appelée sur un thread, renvoie un thread équivalent avec un gestionnaire privé supplémentaire. Le gestionnaire privé est appelé lorsqu'une méthode close()est appelée sur un thread. L'API Stream ainsi que les lambdas et les méthodes de référence ainsi que les méthodes par défaut et statiques dans Java 8 sont la réponse aux paradigmes modernes de développement logiciel. Pour des informations plus détaillées, veuillez vous référer à la documentation officielle .

5.3. API Date/Heure (JSR 310)

Java 8 apporte un nouveau look à la gestion des dates et des heures en fournissant une nouvelle API de date et d'heure (JSR 310) . La manipulation de la date et de l’heure est l’un des pires problèmes pour les développeurs Java. Les normes java.util.Datesuivantes java.util.Calendarn’ont généralement pas amélioré la situation (peut-être même l’ont rendue plus confuse). C'est ainsi qu'est né Joda-Time : une excellente alternative à l'API date/heure pour Java . La nouvelle API Date/Heure de Java 8 (JSR 310) est fortement influencée par Joda-Time et en tire le meilleur parti. Le nouveau package java.timecontient toutes les classes pour la date, l'heure, la date/heure, les fuseaux horaires, les durées et la manipulation de l'heure . La conception de l'API a pris l'immuabilité très au sérieux : les modifications ne sont pas autorisées (une dure leçon tirée de java.util.Calendar). Si une modification est requise, une nouvelle instance de la classe correspondante sera renvoyée. Regardons les principales classes et des exemples de leur utilisation. La première classe Clock, qui permet d'accéder à l'instant courant, à la date et à l'heure en utilisant un fuseau horaire. Clockpeut être utilisé à la place de System.currentTimeMillis()et TimeZone.getDefault().
// Получить системное время How смещение UTC
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
Exemple de sortie de console :
2014-04-12T15:19:29.282Z
1397315969360
Les autres nouvelles classes que nous examinerons sont LocaleDateet LocalTime. LocaleDatecontient uniquement la partie date sans le fuseau horaire dans le système de calendrier ISO-8601. En conséquence, LocalTimeil ne contient qu’une partie du timecode>.
// получить местную date и время время
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );
System.out.println( dateFromClock );

// получить местную date и время время
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );
System.out.println( timeFromClock );
Exemple de sortie de console :
2014-04-12
2014-04-12
11:25:54.568
15:25:54.568
LocalDateTimeconcatène LocaleDateet LocalTimeet contient une date et une heure, mais pas de fuseau horaire, dans le système de calendrier ISO-8601. Un exemple simple est donné ci-dessous.
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );
Exemple de sortie de console :
2014-04-12T11:37:52.309
2014-04-12T15:37:52.309
Si vous avez besoin de la date/heure pour un fuseau horaire spécifique, ZonedDateTime. Il contient la date et l'heure dans le système de calendrier ISO-8601. Voici quelques exemples pour différents fuseaux horaires.
// Получение даты/времени для временной зоны
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );

System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );
Exemple de sortie de console :
2014-04-12T11:47:01.017-04:00[America/New_York]
2014-04-12T15:47:01.017Z
2014-04-12T08:47:01.017-07:00[America/Los_Angeles]
Et enfin, jetons un œil à la classe Duration: durée en secondes et nanosecondes. Cela rend le calcul entre deux dates très simple. Voyons comment procéder :
// Получаем разницу между двумя датами
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );
L'exemple ci-dessus calcule la durée (en jours et heures) entre deux dates, le 16 avril 2014 et le 16 avril 2015 . Voici un exemple de sortie de la console :
Duration in days: 365
Duration in hours: 8783
L'impression générale de la nouvelle date/heure dans Java 8 est très, très positive. En partie parce que les changements sont basés sur une base éprouvée (Joda-Time), en partie parce que cette fois, la question a été sérieusement reconsidérée et les voix des développeurs ont été entendues. Pour plus de détails, veuillez vous référer à la documentation officielle .

5.4. Moteur JavaScript Nashorn

Java 8 est livré avec le nouveau moteur JavaScript Nashorn , qui vous permet de développer et d'exécuter certains types d'applications JavaScript sur la JVM. Le moteur JavaScript Nashorn est simplement une autre implémentation de javax.script.ScriptEngine qui suit le même ensemble de règles pour permettre à Java et JavaScript d'interagir. Voici un petit exemple.
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( "JavaScript" );

System.out.println( engine.getClass().getName() );
System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );
Exemple de sortie de console :
jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2

5.5. Base64

Enfin, le support du codage Base64 a trouvé sa place dans la bibliothèque standard Java avec la sortie de Java 8. Il est très simple à utiliser, l'exemple le démontre.
package com.javacodegeeks.java8.base64;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64s {
    public static void main(String[] args) {
        final String text = "Base64 finally in Java 8!";

        final String encoded = Base64
            .getEncoder()
            .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
        System.out.println( encoded );

        final String decoded = new String(
            Base64.getDecoder().decode( encoded ),
            StandardCharsets.UTF_8 );
        System.out.println( decoded );
    }
}
La sortie console du programme affiche à la fois le texte codé et décodé :
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
Il existe également des classes pour les encodeurs/décodeurs compatibles URL, ainsi que les encodeurs/décodeurs compatibles MIME ( Base64.getUrlEncoder()/ Base64.getUrlDecoder(), Base64.getMimeEncoder()/ Base64.getMimeDecoder()).

5.6. Réseaux parallèles

La version Java 8 ajoute de nombreuses nouvelles méthodes pour le traitement des tableaux parallèles. Le plus important d'entre eux est peut-être parallelSort(), qui peut considérablement accélérer le tri sur les machines multicœurs. Le petit exemple ci-dessous montre la nouvelle famille de méthodes ( parallelXxx) en action.
package com.javacodegeeks.java8.parallel.arrays;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

public class ParallelArrays {
    public static void main( String[] args ) {
        long[] arrayOfLong = new long [ 20000 ];

        Arrays.parallelSetAll( arrayOfLong,
            index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
            i -> System.out.print( i + " " ) );
        System.out.println();

        Arrays.parallelSort( arrayOfLong );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
            i -> System.out.print( i + " " ) );
        System.out.println();
    }
}
Ce petit morceau de code utilise une méthode parallelSetAll()pour remplir un tableau avec 20 000 valeurs aléatoires. Après cela, il est appliqué parallelSort(). Le programme imprime les 10 premiers éléments avant et après le tri pour montrer que le tableau est réellement trié. Un exemple de sortie de programme pourrait ressembler à ceci (notez que les éléments du tableau sont aléatoires).
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793

5.7. Parallélisme

De nouvelles méthodes ont été ajoutées à la classe java.util.concurrent.ConcurrentHashMappour prendre en charge les opérations d'agrégation basées sur les objets de flux et les expressions lambda nouvellement ajoutés. De nouvelles méthodes ont également été ajoutées à la classe java.util.concurrent.ForkJoinPoolpour prendre en charge le pooling partagé (voir aussi notre cours gratuit sur la concurrence Java ). Une nouvelle classe java.util.concurrent.locks.StampedLocka été ajoutée pour fournir un verrouillage basé sur les capacités avec trois modes d'accès pour le contrôle en lecture/écriture (elle peut être considérée comme une meilleure alternative à la moins bonne java.util.concurrent.locks.ReadWriteLock). Nouvelles classes qui ont été ajoutées au package java.util.concurrent.atomic:
  • DoubleAccumulateur
  • DoubleAdder
  • Accumulateur long
  • Additionneur long

6. Nouvelles fonctionnalités dans l'environnement d'exécution Java (JVM)

La zone PermGena été retirée et remplacée par Metaspace (JEP 122). Les options JVM -XX:PermSizeet -XX:MaxPermSizeont été remplacées par -XX:MetaSpaceSizeet -XX:MaxMetaspaceSizerespectivement.

7. Conclusion

L'avenir est là : Java 8 a fait évoluer sa plate-forme en proposant des fonctionnalités qui permettent aux développeurs d'être plus productifs. Il est encore trop tôt pour migrer les systèmes de production vers Java 8, mais l'adoption devrait lentement commencer à se développer au cours des prochains mois. Cependant, il est maintenant temps de commencer à préparer votre base de code pour la compatibilité avec Java 8 et d'être prêt à incorporer les modifications de Java 8 lorsqu'elle sera suffisamment sécurisée et stable. En témoignage de l'acceptation de Java 8 par la communauté, Pivotal a récemment publié le Spring Framework avec un support de production pour Java 8 . Vous pouvez donner votre avis sur les nouvelles fonctionnalités intéressantes de Java 8 dans les commentaires.

8.Sources

Quelques ressources supplémentaires qui traitent en profondeur de divers aspects des fonctionnalités de Java 8 :
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION