Palabra clave var en Java (Inferencia de tipo para variables locales)
Usa var para la inferencia de tipo de variables locales en Java, cuándo mejora la legibilidad y cuándo no.
Desde Java 10, puedes declarar una variable local con var y dejar que el compilador infiera su tipo a partir del inicializador. var greeting = "hello"; es exactamente lo mismo, bytecode compilado incluido, que String greeting = "hello"; — el tipo sigue siendo String, simplemente no lo escribiste dos veces. Esto es inferencia de tipo para variables locales: una conveniencia sintáctica que elimina nombres de tipo redundantes sin hacer Java dinámicamente tipado. Bien utilizada elimina ruido; usada sin cuidado oculta exactamente la información que un lector necesita.
Esta página cubre qué hace y qué no hace var, exactamente dónde es legal, los casos en que resulta beneficioso, los casos en que perjudica la legibilidad, y un programa ejecutable que demuestra que los tipos inferidos son los que esperas.
var es inferencia, no tipado dinámico
El hecho más importante: var no es un nuevo tipo "comodín". El compilador lee el lado derecho, determina el tipo estático y lo fija. A partir de ese momento la variable tiene un tipado tan fuerte como si hubieras escrito el tipo a mano — no puedes reasignarla a un tipo diferente, y el tipo inferido queda fijo en tiempo de compilación.
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 es un nombre de tipo reservado, no una palabra clave — todavía puedes usar var como nombre de variable o método (aunque no deberías). Solo activa la inferencia en la posición de declaración de variable local.
Dónde se permite var — y dónde no
var solo funciona para variables locales que tienen un inicializador. El compilador necesita un lado derecho del que leer el tipo; sin él no hay nada que inferir.
| Posición | ¿var permitido? | Motivo |
|---|---|---|
| Variable local con inicializador | Sí | El inicializador proporciona el tipo |
Índice/elemento en bucles for | Sí | La expresión del bucle proporciona el tipo |
| Variable en try-with-resources | Sí | La expresión del recurso proporciona el tipo |
| Variable local sin inicializador | No | No hay nada de lo que inferir |
| Campos / variables de instancia | No | La inferencia es solo local por diseño |
| Parámetros de método | No | Son los llamadores, no los inicializadores, quienes suministran valores |
| Tipos de retorno de método | No | Misma razón que los parámetros |
Inicializado solo con null | No | null no tiene tipo concreto |
| Parámetros lambda (básicos) | Especial | (var x, var y) -> ... está permitido desde 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 parametersLa verdadera ventaja: reducir los genéricos ruidosos
var justifica su existencia cuando el nombre del tipo es largo, repetido o con muchos genéricos. El caso clásico es una declaración donde el tipo aparece completo en ambos lados 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>>();También resulta útil con iteradores, el Map.Entry que obtienes de un HashMap, y otros tipos verbosos que no aportan claridad cuando se escriben completos:
for (var entry : byCity.entrySet()) { // Map.Entry<String, List<Customer>>
System.out.println(entry.getKey() + " -> " + entry.getValue().size());
}Cuándo NO usar var
var ayuda cuando el tipo es obvio por el lado derecho y perjudica cuando no lo es. Si un lector tiene que ejecutar el código mentalmente para conocer el tipo, escribe el tipo explícitamente.
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 intTen cuidado con la trampa de los literales numéricos: var infiere el tipo del literal, no el tipo que quizás pretendías.
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 cuando pierdas un tipo de interfaz deliberado. var list = new ArrayList<String>(); tipifica list como ArrayList<String>, no como List<String> — correcto localmente, pero si tu intención era programar contra la interfaz, indícalo explícitamente.
Un ejemplo práctico que puedes ejecutar
Este programa ejercita var en todas sus posiciones legales — valores simples, un mapa genérico, un bucle for mejorado, un bucle for con índice — y usa getClass().getSimpleName() para demostrar que los tipos inferidos en tiempo de ejecución son exactamente lo que el lado derecho implicaba.
Lo que puedes extraer de la ejecución:
greeting.getClass().getSimpleName()imprimeString, demostrando quevar greeting = "hello"produjo unStringgenuino —vares inferencia en tiempo de compilación, y en tiempo de ejecución el objeto es exactamente lo que el literal implicaba, nada dinámico al respecto.count + 1 = 43yprice * 2 = 19.98confirman las reglas de inferencia numérica:42hizo quecountfuera unint,9.99hizo quepricefuera undouble. El tipo del literal — no tu intención — decide, lo cual es la trampa a recordar cuando necesitas unlongo unfloat.scores type = HashMapmuestra quevarcapturó el tipo concreto del lado derechoHashMap, no la interfazMap; el diamante<String, List<Integer>>del lado derecho le dio al compilador todo lo necesario aunque el lado izquierdo solo dijeravar.total chars = 10proviene defor (var name : names)dondenamefue inferido comoString, por lo quename.length()se resolvió correctamente (3 + 3 + 4) —varfunciona en bucles for mejorados, infiriendo el tipo del elemento a partir del iterable.0..4 sum = 10proviene defor (var i = 0; ...)dondeifue inferido comointa partir del literal0; el bucle indexado es uno de los lugares más limpios para usarvarporque el tipo es inconfundible.
Práctica
Temas relacionados
- Variables en Java — declaración e inicialización de variables locales.
- Ámbito de variables — por qué
varestá intencionalmente limitado al ámbito local. - Genéricos — los nombres de tipo verbosos que
varoculta mejor. - El bucle
for— dondevar iyvar entryse leen con claridad.