JEP 443, Unnamed Patterns and Variables (Preview)

Contexte

Les motifs pour les enregistrements (JEP 440 - Records Pattern) permettent justement de déconstruire les enregistrements via leurs composants. De ce fait, nous pouvons avoir des noms et des types de composants qui nous n’intéresse pas. Cela nous devons tout de même les définir.

Voici un exemple avec le code suivant :

record Point(int x, int y) { }
enum Couleur { ROUGE, VERT, BLEU }
record PointColore(Point p, Couleur c) { }

var s = new PointColore(new Point(3,4), Couleur.VERT);

if (r instanceof PointColore(Point p, Couleur c)) { (1)
    // nous pouvons utiliser p.x()
    // ainsi que p.y()
}
  1. Il a été nécessaire de définir la variable c de type Couleur

De leur côté, les variables non utilisées sont liées à la programmation impérative.

Voici un exemple pour le traitement des exceptions :

String chaine = ...;
try {
    int entier = Integer.parseInt(chaine);
    // On utilise entier
} catch (NumberFormatException ex) { (1)
    System.out.println("Chaîne n'est pas un nombre : " + chaine);
}
  1. La variable ex de type NumberFormatException doit être définie même si elle n’est pas utilisée.

Principe

Le principe est d’enrichir le langage java avec les motifs sans nom et les variables sans nom. Les deux pourront être notés par le caractère souligné _.

C’est une fonctionnatité en mode aperçu.

astucePour activer ce mode, je vous conseille cette article JEP 12 - Mode Aperçu

Motifs non utilisés

En reprenant, l’exemple ci-dessus sur les enregistrements, nous pouvons utiliser le caractère souligné _ à la place de la variable non utilisée dans le motif suivant :

if (o instanceof Point(_, int y)) {
   // Utilisation de y
   ...
}

Nous pouvons le faire sur plusieurs niveaux de composition. Ci-dessous on ignore le composant Couleur :

if (r instanceof PoinColore(Point(int x, int y), _)) {  (1)
   // Utilisation de x
   // et de y
   ...
}
  1. La composant Couleur ne nous intéresse pas (le nom et le type)

Inversement, cela peut être seulement la couleur qui nous intéresse?

if (r instanceof PoinColore(_, Couleur c)) {
   // Utilisation uniquement de c
   ...
}

ou uniquement la coordonnée x de notre point

if (r instanceof PoinColore(Point(int x, _), _)) {
   // Utilisation seulement de  x
   ...
}

Variables non utilisées

Déclaration d’affectation

C’est le cas simple de l’affectation où la variable n’est pas utilisée..

Par exemple, une liste de nombre où x1, y1, z1 et consorts. A chaque fois, ceux sont les deux premiers nombres qui sont utiles. Nous pouvons écrire le code suivant::

Queue<Integer> q = ... // x1, y1, z1, x2, y2, z2, ...
while (q.size() >= 3) {
   var x = q.remove();
   var y = q.remove();
   var _ = q.remove();
   ... new Point(x, y) ...
}

Et si c’est que la valeur x qui est utile, nous aurions donc cela :

while (q.size() >= 3) {
    var x = q.remove();
    var _ = q.remove();
    var _ = q.remove();
    ... new Point(x, 0) ...
}

Bloc catch

En reprenant l’exemple plus haut, nous n’avons plus besoin de nommer la variable du catch car nous ne l’utilisons pas.

Cela donne le code suivant ;

String chaine = ...;
try {
    int entier = Integer.parseInt(chaine);
    // On utilise entier
} catch (NumberFormatException _) {
    System.out.println("Chaîne n'est pas un nombre : " + chaine);
}

Cela est aussi valable avec plusieurs bloc catch.

String chaine = ...;
try {
    int entier = Integer.parseInt(chaine);
    // On utilise entier
} catch (NumberFormatException _) {
    System.out.println("Chaîne n'est pas un nombre : " + chaine);
} catch (NumberFormatException _) {
} catch (Throwable _) {
}

Construction try-with-ressources

Cela donne simplement le code suivant :

try (var _ = ScopedContext.acquire()) {
    // Aucun utilisation de la ressource acquis précédemment
    ...
}

Boucle

Cela est valable pour les boucles si les variables ne sont pas utilisées, comme dans l’exemple ci-dessous :

int acc = 0;
for (Commande _ : commandes) {
    if (acc < LIMITE) {
        acc++
        ...
    }
}

Lambda

Le caractère souligné _ peut être utilisé si le paramètre non pertinent.

...stream.collect(Collectors.toMap(String::toUpperCase, _ -> "NODATA"))

Histoire du souligné

Le simple souligné _ est une syntaxe valide depuis Java 1.0, dont nous pouvions l’utiliser en tant qu’identifiant.

Le processus qui a permit de réaliser des motifs et variables sans nom aujourd’hui a commencé en 2014 avec Java 8. En effet, à partir de cette version, le compilateur émettait un avertissement si un simple souligné était utilisé comme identifiant.

Puis, en 2017, en java 9 avec la JEP 213: Milling Project Coin, l’avertissement passe à une erreur de compilation pour l’utilisation du simple souligné _.

astuceLes identifiants commençant par le caractère souligné _ est encore valide (si il est suivi de lettres).