W3docs

Le mot-clé var en Java (inférence de type de variable locale)

Utilisez var pour l'inférence de type de variable locale en Java, quand cela améliore la lisibilité et quand ce n'est pas le cas.

Depuis Java 10, vous pouvez déclarer une variable locale avec var et laisser le compilateur inférer son type à partir de l'initialiseur. var greeting = "hello"; est exactement identique, bytecode compilé compris, à String greeting = "hello"; — le type reste String, vous n'avez simplement pas eu à le répéter. C'est ce qu'on appelle l'inférence de type de variable locale : une commodité syntaxique qui supprime les noms de types redondants sans rendre Java dynamiquement typé. Bien utilisé, cela réduit le bruit ; mal utilisé, cela masque précisément les informations dont le lecteur a besoin.

Cette page explique ce que var fait et ne fait pas, exactement où il est autorisé, les cas où il est utile, les cas où il nuit à la lisibilité, et un programme exécutable qui prouve que les types inférés sont bien ceux attendus.

var est de l'inférence, pas du typage dynamique

Le fait le plus important à retenir : var n'est pas un nouveau type "fourre-tout". Le compilateur lit le membre droit, détermine le type statique et le grave dans le marbre. À partir de ce moment, la variable est aussi fortement typée que si vous aviez écrit le type à la main — vous ne pouvez pas la réaffecter à un type non lié, et le type inféré est fixé à la compilation.

var name = "Ada";   // name has static type String, forever
name = "Lovelace";  // fine, still a String
name = 42;          // compile error: int cannot be assigned to String

var est un nom de type réservé, pas un mot-clé — vous pouvez toujours utiliser var comme nom de variable ou de méthode (même si vous ne devriez pas). Il ne déclenche l'inférence que dans la position de déclaration de variable locale.

var est autorisé — et où il ne l'est pas

var ne fonctionne que pour les variables locales qui ont un initialiseur. Le compilateur a besoin d'un membre droit pour lire le type ; sans initialiseur, il n'y a rien à inférer.

Positionvar autorisé ?Raison
Variable locale avec initialiseurOuiL'initialiseur fournit le type
Index/élément dans les boucles forOuiL'expression de boucle fournit le type
Variable de try-with-resourcesOuiL'expression de ressource fournit le type
Variable locale sans initialiseurNonRien à inférer
Champs / variables d'instanceNonL'inférence est locale par conception
Paramètres de méthodeNonLes appelants, pas les initialiseurs, fournissent les valeurs
Types de retour de méthodeNonMême raison que les paramètres
Initialisé à null uniquementNonnull n'a pas de type concret
Paramètres lambda (nus)Spécial(var x, var y) -> ... est autorisé depuis Java 11
var x;                       // error: cannot infer type, no initializer
var nothing = null;          // error: null has no type to infer
public var field = 1;        // error: var not allowed on fields
void m(var p) { }            // error: var not allowed on parameters

Le vrai bénéfice : réduire les génériques verbeux

var mérite sa place quand le nom du type est long, répété, ou chargé de génériques. Le cas classique est une déclaration où le type apparaît en entier des deux côtés du = :

// Before: the type name is written twice
Map<String, List<Customer>> byCity = new HashMap<String, List<Customer>>();

// After: the right side already says everything
var byCity = new HashMap<String, List<Customer>>();

Il brille également avec les itérateurs, le Map.Entry que vous obtenez d'une HashMap, et d'autres types verbeux qui n'apportent aucune clarté quand ils sont explicitement écrits :

for (var entry : byCity.entrySet()) {     // Map.Entry<String, List<Customer>>
  System.out.println(entry.getKey() + " -> " + entry.getValue().size());
}

Quand NE PAS utiliser var

var aide quand le type est évident à partir du membre droit et nuit quand ce n'est pas le cas. Si un lecteur doit exécuter le code mentalement pour connaître le type, écrivez le type explicitement.

var result = service.process(input);   // unclear: what does process return?
Order result = service.process(input); // clear: an Order

var flag = true;                       // fine, obviously boolean
var count = list.size();               // fine, obviously int

Méfiez-vous du piège des littéraux numériques : var infère le type du littéral, pas le type que vous aviez peut-être en tête.

var n = 100;        // int, not long  — for a long you must write 100L or long n
var f = 3.14;       // double, not float
byte b = 1;         // explicit type narrows; var b = 1 would be int

Évitez var lorsqu'il fait perdre un type d'interface délibéré. var list = new ArrayList<String>(); type list comme ArrayList<String>, pas List<String> — acceptable localement, mais si vous souhaitiez programmer selon l'interface, indiquez-le explicitement.

Un exemple concret à exécuter

Ce programme utilise var dans toutes ses positions légales — valeurs simples, une map générique, une boucle for améliorée, une boucle for indexée — et utilise getClass().getSimpleName() pour prouver que les types inférés à l'exécution sont exactement ce que les membres droits impliquaient.

java— editable, runs on the server

Ce qu'il faut retenir de l'exécution :

  • greeting.getClass().getSimpleName() affiche String, prouvant que var greeting = "hello" a produit un vrai Stringvar est une inférence à la compilation, et à l'exécution l'objet est exactement ce que le littéral impliquait, rien de dynamique.
  • count + 1 = 43 et price * 2 = 19.98 confirment les règles d'inférence numérique : 42 a fait de count un int, 9.99 a fait de price un double. Le type du littéral — pas votre intention — décide, ce qui est le piège à retenir quand vous avez besoin d'un long ou d'un float.
  • scores type = HashMap montre que var a capturé le type concret du membre droit HashMap, pas l'interface Map ; le diamant <String, List<Integer>> à droite a fourni au compilateur tout ce dont il avait besoin même si le membre gauche ne disait que var.
  • total chars = 10 provient de for (var name : names)name a été inféré comme String, donc name.length() s'est résolu correctement (3 + 3 + 4) — var fonctionne dans les boucles for améliorées, en inférant le type d'élément à partir de l'itérable.
  • 0..4 sum = 10 provient de for (var i = 0; ...)i a été inféré comme int à partir du littéral 0 ; la boucle indexée est l'un des endroits les plus propices à l'utilisation de var car le type est sans ambiguïté.

Pratique

Pratique
Dans laquelle de ces déclarations 'var' est-il légal et infère-t-il le type indiqué par le commentaire ?
Dans laquelle de ces déclarations 'var' est-il légal et infère-t-il le type indiqué par le commentaire ?

Sujets connexes

Was this page helpful?