API pour lesquelles il vaut enfin la peine de passer de Java 8. Partie 1

Java 8 est de loin la version la plus populaire de Java et le restera pendant un certain temps. Cependant, depuis lors, cinq nouvelles versions de Java ont déjà été publiées (9, 10, 11, 12, 13) et bientôt un autre Java 14. Ces nouvelles versions auront énormément de nouvelles fonctionnalités. Par exemple, si vous comptez dans les JEP, 141 au total ont été implémentés:



Cependant, dans cette série d'articles, il n'y aura pas de liste sèche des PEC. Au lieu de cela, je veux juste parler des API intéressantes qui sont apparues dans les nouvelles versions. Chaque article contiendra 10 API. Dans le choix et l'ordre de ces API, il n'y aura pas de logique et de régularité spécifiques. Ce ne sera que 10 API aléatoires, pas TOP 10 et sans trier de l'API la plus importante à la moins importante. Commençons.


1. Méthodes Objects.requireNonNullElse() et Objects.requireNonNullElseGet()


Introduit dans: Java 9


Nous commençons notre liste avec deux méthodes très simples mais très utiles dans la classe java.util.Objects : requireNonNullElse() et requireNonNullElseGet() . Ces méthodes vous permettent de renvoyer l'objet transmis, s'il n'est pas null , et s'il l'est, renvoyez l'objet par défaut. Par exemple:


 class MyCoder { private final Charset charset; MyCoder(Charset charset) { this.charset = Objects.requireNonNullElse( charset, StandardCharsets.UTF_8); } } 

requireNonNullElseGet() n'est rien de plus qu'une version paresseuse de requireNonNullElse() . Cela peut être utile si le calcul de l'argument par défaut est coûteux:


 class MyCoder { private final Charset charset; MyCoder(Charset charset) { this.charset = Objects.requireNonNullElseGet( charset, MyCoder::defaultCharset); } private static Charset defaultCharset() { // long operation... } } 

Oui, bien sûr, dans les deux cas, on pourrait facilement se passer de ces fonctions, par exemple, en utilisant l'opérateur ternaire habituel ou en Optional , mais en utilisant toujours une fonction spéciale, le code est un peu plus court et plus propre. Et si vous utilisez l'importation statique et écrivez simplement requireNonNullElse() au lieu d' Objects.requireNonNullElse() , alors le code peut être réduit encore plus.



2. Méthodes d'usine renvoyant des collections immuables


Introduit dans: Java 9


Si les deux méthodes précédentes ne sont que des cosmétiques, les méthodes d'usine de collecte statique peuvent vraiment réduire considérablement le code et même améliorer sa sécurité. Ce sont les méthodes suivantes introduites dans Java 9:



À la même liste, vous pouvez ajouter la Map.entry(K k, V v) qui l'accompagne, qui crée une Entry partir de la clé et de la valeur, ainsi que des méthodes de copie des collections apparues dans Java 10:



Les méthodes de fabrique statique vous permettent de créer une collection immuable et de l'initialiser en une seule action:


 List<String> imageExtensions = List.of("bmp", "jpg", "png", "gif"); 

Si vous n'utilisez pas de bibliothèques tierces, un code similaire dans Java 8 semble beaucoup plus lourd:


 List<String> imageExtensions = Collections.unmodifiableList( Arrays.asList("bmp", "jpg", "png", "gif")); 

Et dans le cas de Set ou Map c'est encore plus triste, car il n'y a pas d'analogues de Arrays.asList() pour Set et Map .


Une telle lourdeur incite de nombreuses personnes qui écrivent en Java 8 à abandonner complètement les collections immuables et utilisent toujours les ArrayList , HashSet et HashMap , même lorsque le sens des collections immuables est nécessaire. En conséquence, cela rompt le concept d'immuable par défaut et réduit la sécurité du code.


Si vous effectuez enfin une mise à niveau à partir de Java 8, alors travailler avec des collections immuables devient beaucoup plus facile et plus agréable grâce aux méthodes d'usine.



3. Files.readString() et Files.writeString()


Introduit dans: Java 11


Java a toujours été connu pour son introduction tranquille de méthodes prêtes à l'emploi pour des opérations fréquentes. Par exemple, pour l'une des opérations de programmation les plus populaires, la lecture d'un fichier, il n'y a pas eu pendant longtemps de méthode toute faite. Seulement 15 ans après la sortie de Java 1.0, NIO est apparu, où la méthode Files.readAllBytes() été introduite pour lire le fichier dans un tableau d'octets.


Mais cela n'était pas encore suffisant, car les gens doivent souvent travailler avec des fichiers texte et pour cela, vous devez lire les chaînes du fichier, pas les octets. Par conséquent, dans Java 8, la méthode Files.readAllLines() été ajoutée, renvoyant une List<String> .


Cependant, cela ne suffisait pas, car les gens demandaient à quel point il était facile de lire l'intégralité du fichier sur une seule ligne. En conséquence, pour compléter l'image dans Java 11, la méthode Files.readString() tant attendue a été Files.readString() , fermant ainsi définitivement cette question. Étonnamment, si une méthode similaire était présente dans de nombreuses autres langues dès le début, Java a mis plus de 20 ans à le faire.


Avec readString() bien sûr, la writeString() symétrique writeString() a également été introduite. Ces méthodes ont également des surcharges qui vous permettent de spécifier un jeu de Charset . Ensemble, tout cela rend le travail avec des fichiers texte extrêmement pratique. Un exemple:


 /**        */ private void reencodeFile(Path path, Charset from, Charset to) throws IOException { String content = Files.readString(path, from); Files.writeString(path, content, to); } 


4. Optional.ifPresentOrElse() et Optional.stream()


Introduit dans: Java 9


Lorsque Optional est apparu dans Java 8, ils ne disposaient pas d'un moyen pratique pour effectuer deux actions différentes, selon qu'il a une valeur ou non. En conséquence, les gens doivent recourir à la chaîne habituelle isPresent() et get() :


 Optional<String> opt = ... if (opt.isPresent()) { log.info("Value = " + opt.get()); } else { log.error("Empty"); } 

Ou vous pouvez toujours esquiver de cette façon:


 Optional<String> opt = ... opt.ifPresent(str -> log.info("Value = " + str)); if (opt.isEmpty()) { log.error("Empty"); } 

Les deux options ne sont pas parfaites. Mais, à partir de Java 9, cela peut être fait avec élégance en utilisant la méthode Optional.ifPresentOrElse() :


 Optional<String> opt = ... opt.ifPresentOrElse( str -> log.info("Value = " + str), () -> log.error("Empty")); 

Une autre nouvelle méthode intéressante dans Java 9 est Optional.stream() , qui renvoie un Stream partir d'un élément si la valeur est présente, et un Stream vide si ce n'est pas le cas. Une telle méthode peut être très utile dans les chaînes avec flatMap() . Par exemple, dans cet exemple, il est très simple d'obtenir une liste de tous les numéros de téléphone d'une entreprise:


 class Employee { Optional<String> getPhoneNumber() { ... } } class Department { List<Employee> getEmployees() { ... } } class Company { List<Department> getDepartments() { ... } Set<String> getAllPhoneNumbers() { return getDepartments() .stream() .flatMap(d -> d.getEmployees().stream()) .flatMap(e -> e.getPhoneNumber().stream()) .collect(Collectors.toSet()); } } 

En Java 8, vous devez écrire quelque chose comme:


 e -> e.getPhoneNumber().map(Stream::of).orElse(Stream.empty()) 

Il semble volumineux et peu lisible.



5. Process.pid() , Process.info() et ProcessHandle


Introduit dans: Java 9


Si vous pouvez toujours gérer sans les API précédentes, le remplacement de la méthode Process.pid() dans Java 8 sera assez problématique, en particulier multiplateforme. Cette méthode renvoie l'ID de processus natif:


 Process process = Runtime.getRuntime().exec("java -version"); System.out.println(process.pid()); 

En utilisant la méthode Process.info() , vous pouvez également trouver des informations utiles supplémentaires sur le processus. Il renvoie un objet de type ProcessHandle.Info . Voyons ce qu'il nous retourne pour le processus ci-dessus:


 Process process = Runtime.getRuntime().exec("java -version"); ProcessHandle.Info info = process.info(); System.out.println("PID = " + process.pid()); System.out.println("User = " + info.user()); System.out.println("Command = " + info.command()); System.out.println("Args = " + info.arguments().map(Arrays::toString)); System.out.println("Command Line = " + info.commandLine()); System.out.println("Start Time = " + info.startInstant()); System.out.println("Total Time = " + info.totalCpuDuration()); 

Conclusion:


 PID = 174 User = Optional[orionll] Command = Optional[/usr/lib/jvm/java-13-openjdk-amd64/bin/java] Args = Optional[[-version]] Command Line = Optional[/usr/lib/jvm/java-13-openjdk-amd64/bin/java -version] Start Time = Optional[2020-01-24T05:54:25.680Z] Total Time = Optional[PT0.01S] 

Que faire si le processus n'a pas été démarré à partir du processus Java actuel? Pour cela, ProcessHandle vient à la ProcessHandle . Par exemple, obtenons toutes les mêmes informations pour le processus en cours en utilisant la méthode ProcessHandle.current() :


 ProcessHandle handle = ProcessHandle.current(); ProcessHandle.Info info = handle.info(); System.out.println("PID = " + handle.pid()); System.out.println("User = " + info.user()); System.out.println("Command = " + info.command()); System.out.println("Args = " + info.arguments().map(Arrays::toString)); System.out.println("Command Line = " + info.commandLine()); System.out.println("Start Time = " + info.startInstant()); System.out.println("Total Time = " + info.totalCpuDuration()); 

Conclusion:


 PID = 191 User = Optional[orionll] Command = Optional[/usr/lib/jvm/java-13-openjdk-amd64/bin/java] Args = Optional[[Main.java]] Command Line = Optional[/usr/lib/jvm/java-13-openjdk-amd64/bin/java Main.java] Start Time = Optional[2020-01-24T05:59:17.060Z] Total Time = Optional[PT1.56S] 

Pour obtenir un ProcessHandle pour n'importe quel processus par son PID, vous pouvez utiliser la méthode ProcessHandle.of() (il retournera Optional.empty si le processus n'existe pas).


Il existe également de nombreuses autres méthodes intéressantes dans ProcessHandle , par exemple ProcessHandle.allProcesses() .



6. Méthodes de String : isBlank() , strip() , stripLeading() , stripTrailing() , repeat() et lines()


Introduit dans: Java 11


Une montagne entière de méthodes utiles pour les chaînes est apparue dans Java 11.


La méthode String.isBlank() vous permet de savoir si une chaîne se compose uniquement d'espaces:


 System.out.println(" \n\r\t".isBlank()); // true 

Les String.stripLeading() , String.stripTrailing() et String.strip() suppriment les espaces blancs au début d'une ligne, à la fin d'une ligne ou aux deux extrémités:


 String str = " \tHello, world!\t\n"; String str1 = str.stripLeading(); // "Hello, world!\t\n" String str2 = str.stripTrailing(); // " \tHello, world!" String str3 = str.strip(); // "Hello, world!" 

Notez que String.strip() pas identique à String.trim() : le second supprime uniquement les caractères dont le code est inférieur ou égal à U + 0020, et le premier supprime également les espaces d'Unicode:


 System.out.println("str\u2000".strip()); // "str" System.out.println("str\u2000".trim()); // "str\u2000" 


La méthode String.repeat() concatène la chaîne elle-même n fois:


 System.out.print("Hello, world!\n".repeat(3)); 

Conclusion:


 Hello, world! Hello, world! Hello, world! 

Enfin, la méthode String.lines() décompose la chaîne en lignes. Au revoir String.split() , avec lequel les gens confondent constamment, quel argument utiliser pour cela, soit "\n" , soit "\r" ou "\n\r" (en fait, il est préférable d'utiliser régulièrement expression "\R" , qui couvre toutes les combinaisons). De plus, String.lines() peut souvent être plus efficace car il renvoie des lignes paresseusement.


 System.out.println("line1\nline2\nline3\n" .lines() .map(String::toUpperCase) .collect(Collectors.joining("\n"))); 

Conclusion:


 LINE1 LINE2 LINE3 


7. String.indent()


Apparu dans: Java 12


Diluons notre histoire avec quelque chose de nouveau qui est apparu récemment. Meet: la méthode String.indent() , qui augmente (ou diminue) l'indentation de chaque ligne d'une ligne donnée de la valeur spécifiée. Par exemple:


 String body = "<h1>Title</h1>\n" + "<p>Hello, world!</p>"; System.out.println("<html>\n" + " <body>\n" + body.indent(4) + " </body>\n" + "</html>"); 

Conclusion:


 <html> <body> <h1>Title</h1> <p>Hello, world!</p> </body> </html> 

Notez que pour la dernière ligne, String.indent() lui-même inséré le String.indent() ligne, nous n'avons donc pas eu à ajouter '\n' après body.indent(4) .


Bien sûr, une telle méthode sera plus intéressante en combinaison avec des blocs de texte lorsqu'ils deviendront stables, mais rien ne nous empêche de l'utiliser dès maintenant sans aucun bloc de texte.



8. Méthodes de Stream : takeWhile() , dropWhile() , iterate() avec un prédicat et ofNullable()


Introduit dans: Java 9


Stream.takeWhile() est similaire à Stream.limit() , mais restreint Stream non pas par quantité, mais par prédicat. Un tel besoin de programmation apparaît très souvent. Par exemple, si nous devons obtenir toutes les entrées du journal pour l'année en cours:


 [ { "date" : "2020-01-27", "text" : "..." }, { "date" : "2020-01-25", "text" : "..." }, { "date" : "2020-01-22", "text" : "..." }, { "date" : "2020-01-17", "text" : "..." }, { "date" : "2020-01-11", "text" : "..." }, { "date" : "2020-01-02", "text" : "..." }, { "date" : "2019-12-30", "text" : "..." }, { "date" : "2019-12-27", "text" : "..." }, ... ] 

Stream enregistrements est presque sans fin, donc filter() ne peut pas être utilisé. Puis takeWhile() vient à la takeWhile() :


 getNotesStream() .takeWhile(note -> note.getDate().getYear() == 2020); 

Et si nous voulons obtenir des enregistrements pour 2019, nous pouvons utiliser dropWhile() :


 getNotesStream() .dropWhile(note -> note.getDate().getYear() == 2020) .takeWhile(note -> note.getDate().getYear() == 2019); 

Dans Java 8, Stream.iterate() ne pouvait générer qu'un Stream infini. Mais en Java 9, cette méthode a une qui prend un prédicat. Grâce à cela, de nombreuses boucles for peuvent désormais être remplacées par Stream :


 // Java 8 for (int i = 1; i < 100; i *= 2) { System.out.println(i); } 

 // Java 9+ IntStream .iterate(1, i -> i < 100, i -> i * 2) .forEach(System.out::println); 

Ces deux versions impriment tous les degrés d'une égalité qui ne dépassent pas 100 :


 1 2 4 8 16 32 64 

Soit dit en passant, le dernier code pourrait être réécrit en utilisant takeWhile() :


 IntStream .iterate(1, i -> i * 2) .takeWhile(i -> i < 100) .forEach(System.out::println); 

Cependant, l'option avec l' iterate() trois arguments iterate() est toujours plus propre (et IntelliJ IDEA suggère de la corriger).


Enfin, Stream.ofNullable() renvoie un Stream avec un élément s'il n'est pas null et un Stream vide s'il est null . Cette méthode est parfaite dans l'exemple ci-dessus avec les téléphones d'entreprise si getPhoneNumber() retournera une String nullable au lieu de Optional<String> :


 class Employee { String getPhoneNumber() { ... } } class Department { List<Employee> getEmployees() { ... } } class Company { List<Department> getDepartments() { ... } Set<String> getAllPhoneNumbers() { return getDepartments() .stream() .flatMap(d -> d.getEmployees().stream()) .flatMap(e -> Stream.ofNullable(e.getPhoneNumber())) .collect(Collectors.toSet()); } } 


9. Predicate.not()


Apparu dans: Java 11


Cette méthode n'apporte rien de fondamentalement nouveau et est plus esthétique que fondamentale. Néanmoins, la possibilité de raccourcir légèrement le code est toujours très agréable. En utilisant Predicate.not() lambdas qui ont une négation peuvent être remplacés par des références de méthode:


 Files.lines(path) .filter(str -> !str.isEmpty()) .forEach(System.out::println); 

Et maintenant en utilisant not() :


 Files.lines(path) .filter(not(String::isEmpty)) .forEach(System.out::println); 

Oui, les économies ne sont pas si énormes, et si vous utilisez s -> !s.isEmpty() , alors le nombre de caractères, au contraire, devient plus grand. Mais même dans ce cas, je préfère toujours la deuxième option, car elle est plus déclarative et n'utilise pas de variable, ce qui signifie que l'espace de noms n'est pas encombré.



10. Nettoyant


Apparu dans: Java 9


Je veux terminer l'histoire d'aujourd'hui avec une nouvelle API intéressante qui est apparue dans Java 9 et sert à nettoyer les ressources avant qu'elles ne soient éliminées par le garbage collector. Cleaner est un remplacement sûr de la méthode Object.finalize() , elle-même devenue obsolète dans Java 9.


En utilisant Cleaner vous pouvez enregistrer un nettoyage de ressource qui se produira si vous avez oublié de le faire explicitement (par exemple, vous avez oublié d'appeler la méthode close() ou vous n'avez pas utilisé try-with-resources ). Voici un exemple de ressource abstraite pour laquelle une action de nettoyage est enregistrée dans le constructeur:


 public class Resource implements Closeable { private static final Cleaner CLEANER = Cleaner.create(); private final Cleaner.Cleanable cleanable; public Resource() { cleanable = CLEANER.register(this, () -> { //   // (,  ) }); } @Override public void close() { cleanable.clean(); } } 

Dans le bon sens, les utilisateurs devraient créer une telle ressource dans le bloc try :


 try (var resource = new Resource()) { //   } 

Cependant, il peut y avoir des utilisateurs qui oublient de le faire et écrivent simplement var resource = new Resource() . Dans de tels cas, le nettoyage ne sera pas effectué immédiatement, mais sera appelé plus tard dans l'un des cycles de collecte des ordures suivants. C’est mieux que rien.


Si vous voulez mieux étudier Cleaner et découvrir pourquoi vous ne devriez jamais utiliser finalize() , alors je vous recommande d'écouter mon exposé sur ce sujet.



Conclusion


Java ne s'arrête pas et se développe progressivement. Pendant que vous êtes assis sur Java 8, avec chaque version, il y a de plus en plus de nouvelles API intéressantes. Aujourd'hui, nous avons examiné 10 de ces API. Et vous pouvez tous les utiliser si vous décidez enfin de migrer de Java 8.


La prochaine fois, nous examinerons 10 nouvelles API supplémentaires.


Si vous ne voulez pas sauter la partie suivante, je vous recommande de vous abonner à ma chaîne Telegram , où je publie également des nouvelles Java.

Source: https://habr.com/ru/post/fr485750/


All Articles