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 Stringvar è 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.
| Posizione | var consentito? | Motivo |
|---|---|---|
| Variabile locale con inizializzatore | Sì | L'inizializzatore fornisce il tipo |
Indice/elemento nei cicli for | Sì | L'espressione del ciclo fornisce il tipo |
| Variabile try-with-resources | Sì | L'espressione della risorsa fornisce il tipo |
| Variabile locale senza inizializzatore | No | Nulla da cui inferire |
| Campi / variabili di istanza | No | L'inferenza è solo locale per design |
| Parametri di metodo | No | I chiamanti, non gli inizializzatori, forniscono i valori |
| Tipi di ritorno dei metodi | No | Stesso motivo dei parametri |
Inizializzata solo a null | No | null 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 parametersIl 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 intAttenzione 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 intEvita 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.
Cosa ricavare dall'esecuzione:
greeting.getClass().getSimpleName()stampaString, dimostrando chevar greeting = "hello"ha prodotto una veraString—varè inferenza a tempo di compilazione e a runtime l'oggetto è esattamente quello implicato dal letterale, nulla di dinamico.count + 1 = 43eprice * 2 = 19.98confermano le regole di inferenza numerica:42ha resocountunint,9.99ha resopriceundouble. Il tipo del letterale — non la tua intenzione — decide, che è la trappola da ricordare quando hai bisogno di unlongo di unfloat.scores type = HashMapmostra chevarha catturato il tipo concreto del lato destroHashMap, non l'interfacciaMap; il diamante<String, List<Integer>>a destra ha fornito al compilatore tutto il necessario anche se il lato sinistro diceva solovar.total chars = 10proviene dafor (var name : names)dovenameè stato inferito comeString, quindiname.length()è stato risolto correttamente (3 + 3 + 4) —varfunziona nei cicli for-each, inferendo il tipo dell'elemento dall'iterabile.0..4 sum = 10proviene dafor (var i = 0; ...)doveiè stato inferito comeintdal letterale0; il ciclo indicizzato è uno dei posti più puliti per usarevarperché il tipo è inequivocabile.
Esercitati
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
varnasconde meglio. - Il ciclo
for— dovevar ievar entrysi leggono chiaramente.