W3docs

La parola chiave var in Java (inferenza del tipo di variabile locale)

Usa var per l'inferenza del tipo di variabile locale in Java, quando migliora la leggibilità e quando non lo fa.

Da Java 10, puoi dichiarare una variabile locale con var e lasciare che il compilatore inferisca il tipo dall'inizializzatore. var greeting = "hello"; è esattamente uguale, bytecode compilato incluso, a String greeting = "hello"; — il tipo è ancora String, semplicemente non l'hai scritto due volte. Questa è l'inferenza del tipo di variabile locale: una comodità sintattica che elimina i nomi di tipo ridondanti senza rendere Java a tipizzazione dinamica. Usata bene rimuove il rumore; usata con superficialità nasconde proprio le informazioni di cui un lettore ha bisogno.

Questa pagina spiega cosa fa e non fa var, esattamente dove è legale, i casi in cui conviene, i casi in cui danneggia la leggibilità e un programma eseguibile che dimostra che i tipi inferiti sono quelli attesi.

var è inferenza, non tipizzazione dinamica

Il fatto più importante: var non è un nuovo tipo "qualsiasi". Il compilatore legge il lato destro, determina il tipo statico e lo fissa. Da quel momento la variabile è fortemente tipizzata esattamente come se avessi scritto il tipo a mano — non puoi riassegnarla a un tipo non correlato e il tipo inferito è fisso al momento della compilazione.

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 è un nome di tipo riservato, non una parola chiave — puoi ancora usare var come nome di variabile o metodo (anche se non dovresti). Attiva l'inferenza solo nella posizione di dichiarazione di variabile locale.

Dove var è consentito — e dove non lo è

var funziona solo per variabili locali che hanno un inizializzatore. Il compilatore ha bisogno di un lato destro da cui leggere il tipo; senza di esso non c'è nulla da inferire.

Posizionevar consentito?Motivo
Variabile locale con inizializzatoreL'inizializzatore fornisce il tipo
Indice/elemento nei cicli forL'espressione del ciclo fornisce il tipo
Variabile try-with-resourcesL'espressione della risorsa fornisce il tipo
Variabile locale senza inizializzatoreNoNulla da cui inferire
Campi / variabili di istanzaNoL'inferenza è solo locale per design
Parametri di metodoNoI chiamanti, non gli inizializzatori, forniscono i valori
Tipi di ritorno dei metodiNoStesso motivo dei parametri
Inizializzata solo a nullNonull non ha tipo concreto
Parametri lambda (semplici)Speciale(var x, var y) -> ... è consentito da 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

Il vero vantaggio: eliminare i generici rumorosi

var vale il suo peso quando il nome del tipo è lungo, ripetuto o ricco di generici. Il caso classico è una dichiarazione in cui il tipo appare per intero su entrambi i lati del =:

// 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>>();

Brilla anche con gli iteratori, il Map.Entry che si ottiene da una HashMap e altri tipi verbosi che non aggiungono chiarezza quando sono scritti per esteso:

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

Quando NON usare var

var aiuta quando il tipo è ovvio dal lato destro e ostacola quando non lo è. Se un lettore deve eseguire il codice mentalmente per conoscere il tipo, scrivi esplicitamente il tipo.

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

Attenzione alla trappola dei letterali numerici: var inferisce il tipo del letterale, non il tipo che potresti aver inteso.

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

Evita var quando si perde un tipo di interfaccia deliberato. var list = new ArrayList<String>(); tipizza list come ArrayList<String>, non List<String> — va bene localmente, ma se intendevi programmare verso l'interfaccia, dillo esplicitamente.

Un esempio pratico eseguibile

Questo programma esercita var in tutte le sue posizioni legali — valori semplici, una mappa generica, un ciclo for-each, un ciclo for indicizzato — e usa getClass().getSimpleName() per dimostrare che i tipi runtime inferiti sono esattamente quelli implicati dai lati destri.

java— editable, runs on the server

Cosa ricavare dall'esecuzione:

  • greeting.getClass().getSimpleName() stampa String, dimostrando che var greeting = "hello" ha prodotto una vera Stringvar è inferenza a tempo di compilazione e a runtime l'oggetto è esattamente quello implicato dal letterale, nulla di dinamico.
  • count + 1 = 43 e price * 2 = 19.98 confermano le regole di inferenza numerica: 42 ha reso count un int, 9.99 ha reso price un double. Il tipo del letterale — non la tua intenzione — decide, che è la trappola da ricordare quando hai bisogno di un long o di un float.
  • scores type = HashMap mostra che var ha catturato il tipo concreto del lato destro HashMap, non l'interfaccia Map; il diamante <String, List<Integer>> a destra ha fornito al compilatore tutto il necessario anche se il lato sinistro diceva solo var.
  • total chars = 10 proviene da for (var name : names) dove name è stato inferito come String, quindi name.length() è stato risolto correttamente (3 + 3 + 4) — var funziona nei cicli for-each, inferendo il tipo dell'elemento dall'iterabile.
  • 0..4 sum = 10 proviene da for (var i = 0; ...) dove i è stato inferito come int dal letterale 0; il ciclo indicizzato è uno dei posti più puliti per usare var perché il tipo è inequivocabile.

Esercitati

Pratica
In quale di queste dichiarazioni 'var' è legale e inferisce il tipo indicato nel commento?
In quale di queste dichiarazioni 'var' è legale e inferisce il tipo indicato nel commento?

Argomenti correlati

  • Variabili Java — dichiarazione e inizializzazione di variabili locali.
  • Scope delle variabili — perché var è intenzionalmente limitato allo scope locale.
  • Generici — i nomi di tipo verbosi che var nasconde meglio.
  • Il ciclo for — dove var i e var entry si leggono chiaramente.
Was this page helpful?