Java 11 n'a introduit aucune fonctionnalité innovante, mais il contient plusieurs joyaux dont vous n'avez peut-être pas encore entendu parler. Vous avez déjà regardé les dernières nouveautés en matière de String
, Optional
, de Collection
et autres chevaux de bataille? Sinon, alors vous êtes à l'adresse: aujourd'hui, nous allons voir 11 joyaux cachés de Java 11!
Inférence de type pour les paramètres lambda
Lors de l'écriture d'une expression lambda, vous pouvez choisir entre spécifier explicitement des types et les ignorer:
Function<String, String> append = string -> string + " "; Function<String, String> append = (String s) -> s + " ";
Java 10 a introduit var
, mais il ne pouvait pas être utilisé dans lambdas:
En Java 11, c'est déjà possible. Mais pourquoi? Il ne semble pas que var
donné plus qu'une simple passe de type. Bien que ce soit le cas, l'utilisation de var
présente deux avantages mineurs:
- rend l'utilisation de
var
plus universelle en supprimant l'exception à la règle - vous permet d'ajouter des annotations au type de paramètre sans avoir à utiliser son nom complet
Voici un exemple du deuxième cas:
List<EnterpriseGradeType<With, Generics>> types = ; types .stream()
Bien que le mélange de types dérivés, explicites et implicites dans des expressions lambda de la forme (var type, String option, index) -> ...
puisse être implémenté, mais ( dans le cadre de JEP-323 ) ce travail n'a pas été effectué. Par conséquent, il est nécessaire de choisir l'une des trois approches et d'y adhérer pour tous les paramètres de l'expression lambda. La nécessité de spécifier var
pour tous les paramètres afin d'ajouter une annotation pour l'un d'eux peut être légèrement ennuyeuse, mais généralement tolérable.
Traitement de flux de chaînes avec 'String::lines'
Vous avez une chaîne multi-lignes? Vous voulez faire quelque chose avec chaque ligne? Alors String::lines
est le bon choix:
var multiline = "\r\n\r\n\r\n"; multiline .lines()
Notez que la ligne d'origine utilise les délimiteurs Windows \r\n
, et bien que je sois sous Linux, lines()
quand même cassé. Cela est dû au fait que, malgré le système d'exploitation actuel, cette méthode interprète \r
, \n
et \r\n
comme des sauts de ligne - même s'ils sont mélangés sur la même ligne.
Un flux de lignes ne contient jamais les séparateurs de lignes eux-mêmes. Les lignes peuvent être vides ( "\n\n \n\n"
, qui contient 5 lignes), mais la dernière ligne de la ligne d'origine est ignorée si elle est vide ( "\n\n"
; 2 lignes). (Remarque du traducteur: il est pratique pour eux d'avoir une line
, mais d'avoir une string
, et nous avons les deux.)
Contrairement à split("\R")
, les lines()
paresseuses et, je cite , "offrent de meilleures performances [...] en recherchant plus rapidement de nouveaux sauts de ligne". (Si quelqu'un veut déposer un benchmark sur JMH pour vérification, faites le moi savoir). Il reflète également mieux l'algorithme de traitement et utilise une structure de données plus pratique (flux au lieu d'un tableau).
Suppression d'espaces avec 'String::strip'
, etc.
Initialement, String
avait une méthode de découpage pour supprimer les espaces blancs, ce qui était considéré comme tout avec des codes jusqu'à U+0020
. Oui, BACKSPACE
( U+0008)
est un espace blanc comme BELL
( U+0007
), mais LINE SEPARATOR
( U+2028
) n'est plus considéré comme tel.
Java 11 a introduit la méthode strip
, dont l'approche a plus de nuances. Il utilise la Character::isWhitespace
de Java 5 pour déterminer exactement ce qui doit être supprimé. D'après sa documentation, il est clair que ceci:
SPACE SEPARATOR
, LINE SEPARATOR
, PARAGRAPH SEPARATOR
, mais pas un espace inextricableHORIZONTAL TABULATION
( U+0009
), LINE FEED
U+000A
LINE FEED
( U+000A
), VERTICAL TABULATION
( U+000B
), FORM FEED
( U+000C
), CARRIAGE RETURN
( U+000D
)FILE SEPARATOR
U+001C
( U+001C
), U+001C
GROUP SEPARATOR
( U+001D
), U+001D
RECORD SEPARATOR
( U+001E
), U+001E
UNIT SEPARATOR
( U+001F
)
Avec la même logique, il existe deux autres méthodes de nettoyage, stripLeading
et stripTailing
, qui font exactement ce que l'on attend d'eux.
Et enfin, si vous avez juste besoin de savoir si la ligne devient vide après avoir supprimé les espaces, alors il n'est pas nécessaire de vraiment les supprimer - utilisez simplement isBlank
:
" ".isBlank();
Répétition de chaînes avec 'String::repeat'
Saisissez l'idée:
Étape 1: Surveiller JDK

Étape 2: Recherche de questions liées à StackOverflow

Étape 3: arriver avec une nouvelle réponse basée sur les changements futurs

Étape 4: ????
Étape 4: Profit

Comme vous pouvez l'imaginer, String
a une nouvelle méthode de repeat(int)
. Cela fonctionne exactement conformément aux attentes, et il y a peu de choses à discuter.
Créer des chemins avec 'Path::of'
J'aime vraiment l'API Path
, mais convertir des chemins entre différentes vues (comme Path
, File
, URL
, URI
et String
) est toujours ennuyeux. Ce point est devenu moins déroutant en Java 11 en copiant deux méthodes Paths::get
dans Path::of
methods:
Path tmp = Path.of("/home/nipa", "tmp"); Path codefx = Path.of(URI.create("http://codefx.org"));
Ils peuvent être considérés comme canoniques, car les deux anciennes méthodes Paths::get
utilisent de nouvelles options.
Lire et écrire des fichiers avec 'Files::readString'
et 'Files::writeString'
Si j'ai besoin de lire à partir d'un fichier volumineux, j'utilise généralement Files::lines
pour obtenir un flux paresseux de ses lignes. De même, pour enregistrer une grande quantité de données qui peuvent ne pas être entièrement stockées en mémoire, j'utilise Files::write
passant comme Iterable<String>
.
Mais qu'en est-il du cas simple lorsque je veux traiter le contenu d'un fichier sur une seule ligne? Ce n'est pas très pratique, car Files::readAllBytes
et les variantes appropriées de Files::write
fonctionnent sur des tableaux d'octets.
Et puis Java 11 apparaît, ajoutant readString
et writeString
aux Files
:
String haiku = Files.readString(Path.of("haiku.txt")); String modified = modify(haiku); Files.writeString(Path.of("haiku-mod.txt"), modified);
Clair et facile à utiliser. Si nécessaire, vous pouvez passer le jeu de Charset
à readString
, et dans writeString
également un tableau OpenOptions
.
E / S vides avec 'Reader::nullReader'
, etc.
Besoin d'un OutputStream
qui n'écrit nulle part? Ou un InputStream
vide? Et Reader
et Writer
qui ne font rien? Java 11 a tout pour plaire:
InputStream input = InputStream.nullInputStream(); OutputStream output = OutputStream.nullOutputStream(); Reader reader = Reader.nullReader(); Writer writer = Writer.nullWriter();
(Note du traducteur: dans commons-io
ces classes existent depuis environ 2014.)
Cependant, je suis surpris - null
est-il vraiment le meilleur préfixe? Je n'aime pas comment cela est utilisé pour signifier "absence intentionnelle" ... Peut-être serait-il préférable d'utiliser noOp
? (Note du traducteur: ce préfixe a probablement été choisi en raison de l'utilisation courante de /dev/null
.)
{ } ~> [ ]
avec 'Collection::toArray'
Comment convertir des collections en tableaux?
La première option, les objects
, perd toutes les informations sur les types, elle est donc en vol. Et le reste? Les deux sont volumineux, mais le premier est plus court. Ce dernier crée un tableau de la taille requise, de sorte qu'il semble plus productif (c'est-à-dire qu'il "semble plus productif", voir crédibilité ). Mais est-ce vraiment plus productif? Non, au contraire, c'est plus lent (pour le moment).
Mais pourquoi devrais-je m'en soucier? N'y a-t-il pas une meilleure façon de procéder? Dans Java 11, il y a:
String[] strings_fun = list.toArray(String[]::new);
Une nouvelle variante de Collection::toArray
, qui accepte IntFunction<T[]>
, c'est-à-dire une fonction qui reçoit la taille du tableau et renvoie un tableau de la taille requise. Il peut être brièvement exprimé en référence à un constructeur de la forme T[]::new
(pour un T
bien connu).
Fait intéressant, l'implémentation par défaut de Collection#toArray(IntFunction<T[]>)
passe toujours 0
au générateur de tableau. Au début, j'ai décidé que cette solution était basée sur les meilleures performances pour les tableaux de longueur nulle, mais maintenant je pense que la raison peut être que pour certaines collections, le calcul de la taille peut être une opération très coûteuse et vous ne devez pas utiliser cette approche dans l'implémentation par défaut de Collection
. Cependant, des implémentations de collections spécifiques, telles que ArrayList
, peuvent changer cette approche, mais elles ne changent pas dans Java 11. Pas la peine, je suppose.
'Optional::isEmpty'
absence avec 'Optional::isEmpty'
Avec l'utilisation abondante d' Optional
, en particulier dans les grands projets, où vous rencontrez souvent une approche non Optional
, vous devez souvent vérifier si elle a une valeur. Il existe une méthode Optional::isPresent
pour cela. Mais tout aussi souvent, vous devez savoir le contraire - cette Optional
vide. Pas de problème, utilisez simplement !opt.isPresent()
, non?
Bien sûr, il peut en être ainsi, mais il est presque toujours plus facile de comprendre la logique if
sa condition n'est pas inversée. Et parfois, Optional
apparaît à la fin d'une longue chaîne d'appels et si vous avez besoin de le vérifier pour rien, alors vous devez parier !
au tout début:
public boolean needsToCompleteAddress(User user) { return !getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isPresent(); }
Dans ce cas, sautez-le !
très facile. À partir de Java 11, il existe une meilleure option:
public boolean needsToCompleteAddress(User user) { return getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isEmpty(); }
Inverser les prédicats avec 'Predicate::not'
En parlant d'inverser ... L'interface Predicate
a une negate
instance de negate
: elle retourne un nouveau prédicat qui effectue le même contrôle, mais inverse son résultat. Malheureusement, j'arrive rarement à l'utiliser ...
Le problème est que j'ai rarement accès à l'instance de Predicate
. Plus souvent, je veux obtenir une telle instance via un lien vers une méthode (et l'inverser), mais pour que cela fonctionne, le compilateur doit savoir à quoi apporter la référence à la méthode - sans lui, il ne peut rien faire. Et c'est exactement ce qui se passe si vous utilisez la construction (String::isBlank).negate()
: le compilateur ne sait plus ce que String::isBlank
devrait être là-dessus et abandonne. Une caste correctement spécifiée corrige cela, mais à quel prix?
Bien qu'il existe une solution simple. N'utilisez pas la negate
instance de negate
, mais utilisez la nouvelle méthode statique Predicate.not(Predicate<T>)
de Java 11:
Stream .of("a", "b", "", "c")
Déjà mieux!
Expressions régulières en tant que prédicat avec 'Pattern::asMatchPredicate'
Y a-t-il une expression régulière? Besoin de filtrer les données dessus? Que diriez-vous de ceci:
Pattern nonWordCharacter = Pattern.compile("\\W"); Stream .of("Metallica", "Motörhead") .filter(nonWordCharacter.asPredicate()) .forEach(System.out::println);
J'étais très heureux de trouver cette méthode! Il vaut la peine d'ajouter qu'il s'agit d'une méthode de Java 8. Oups, je l'ai ratée alors. Java 11 a ajouté une autre méthode similaire: Pattern::asMatchPredicate
. Quelle est la différence?
asPredicate
vérifie que la chaîne ou une partie de la chaîne correspond au modèle (fonctionne comme s -> this.matcher(s).find()
)asMatchPredicate
vérifie que la chaîne entière correspond au modèle (fonctionne comme s -> this.matcher(s).matches()
)
Par exemple, nous avons une expression régulière qui vérifie les numéros de téléphone, mais elle ne contient pas ^
et $
pour suivre le début et la fin d'une ligne. Ensuite, le code suivant ne fonctionnera pas comme vous pouvez vous y attendre:
prospectivePhoneNumbers .stream() .filter(phoneNumberPatter.asPredicate()) .forEach(this::robocall);
Avez-vous remarqué une erreur? Une ligne comme " -152 ? +1-202-456-1414"
sera filtrée, car elle contient un numéro de téléphone valide. D'un autre côté, Pattern::asMatchPredicate
ne le permettra pas, car la chaîne entière ne correspondra plus au modèle.
Autotest
Voici un aperçu de toutes les onze perles - vous souvenez-vous toujours de ce que fait chaque méthode? Si oui, vous avez réussi le test.
- en
String
:
Stream<String> lines()
String strip()
String stripLeading()
String stripTrailing()
boolean isBlank()
String repeat(int)
- dans
Path
:
static Path of(String, String...)
static Path of(URI)
- dans les
Files
:
String readString(Path) throws IOException
Path writeString(Path, CharSequence, OpenOption...) throws IOException
Path writeString(Path, CharSequence, Charset, OpenOption...) throws IOException
- dans
InputStream
: static InputStream nullInputStream()
- dans
OutputStream
: static OutputStream nullOutputStream()
- dans
Reader
: Reader
static Reader nullReader()
- dans
Writer
: static Writer nullWriter()
- dans
Collection
: T[] toArray(IntFunction<T[]>)
- en
Optional
: boolean isEmpty()
- dans le
Predicate
: Predicate
static Predicate<T> not(Predicate<T>)
- dans le
Pattern
: Predicate<String> asMatchPredicate()
Amusez-vous avec Java 11!