Palavra-chave var no Java (Inferência de Tipo de Variável Local)
Use var para inferência de tipo de variável local em Java, quando melhora a legibilidade e quando não melhora.
Desde o Java 10, você pode declarar uma variável local com var e deixar o compilador inferir seu tipo a partir do inicializador. var greeting = "hello"; é exatamente o mesmo, bytecode compilado e tudo mais, que String greeting = "hello"; — o tipo ainda é String, você simplesmente não o escreveu duas vezes. Isso é inferência de tipo de variável local: uma conveniência sintática que elimina nomes de tipo redundantes sem tornar o Java dinamicamente tipado. Usado corretamente, remove ruído; usado descuidadamente, esconde exatamente a informação que o leitor precisa.
Esta página cobre o que var faz e não faz, exatamente onde é permitido, os casos em que vale a pena, os casos em que prejudica a legibilidade, e um programa executável que prova que os tipos inferidos são o que você espera.
var é inferência, não tipagem dinâmica
O fato mais importante: var não é um novo tipo "qualquer coisa". O compilador lê o lado direito, determina o tipo estático e o incorpora. A partir desse ponto, a variável é tão fortemente tipada quanto se você tivesse escrito o tipo manualmente — você não pode reatribuí-la a um tipo não relacionado, e o tipo inferido é fixado em tempo de compilação.
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 é um nome de tipo reservado, não uma palavra-chave — você ainda pode usar var como nome de variável ou método (embora não deva). Ele só aciona a inferência na posição de declaração de variável local.
Onde var é permitido — e onde não é
var funciona apenas para variáveis locais que possuem um inicializador. O compilador precisa de um lado direito para ler o tipo; sem ele, não há nada a inferir.
| Posição | var permitido? | Motivo |
|---|---|---|
| Variável local com inicializador | Sim | O inicializador fornece o tipo |
Índice/elemento em loops for | Sim | A expressão do loop fornece o tipo |
| Variável em try-with-resources | Sim | A expressão do recurso fornece o tipo |
| Variável local sem inicializador | Não | Nada a inferir |
| Campos / variáveis de instância | Não | A inferência é somente local por design |
| Parâmetros de método | Não | Chamadores, não inicializadores, fornecem valores |
| Tipos de retorno de método | Não | Mesmo motivo que parâmetros |
Inicializado apenas com null | Não | null não tem tipo concreto |
| Parâmetros lambda (simples) | Especial | (var x, var y) -> ... é permitido desde o 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 parametersO verdadeiro benefício: simplificar genéricos verbosos
var se justifica quando o nome do tipo é longo, repetido ou repleto de genéricos. O caso clássico é uma declaração em que o tipo aparece completo nos dois lados do =:
// 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>>();Também brilha com iteradores, o Map.Entry que você obtém de um HashMap, e outros tipos verbosos que não agregam clareza quando escritos por extenso:
for (var entry : byCity.entrySet()) { // Map.Entry<String, List<Customer>>
System.out.println(entry.getKey() + " -> " + entry.getValue().size());
}Quando NÃO usar var
var ajuda quando o tipo é óbvio a partir do lado direito e atrapalha quando não é. Se o leitor precisar executar o código mentalmente para saber o tipo, escreva o tipo explicitamente.
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 intCuidado com a armadilha do literal numérico: var infere o tipo do literal, não o tipo que você pode ter pretendido.
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 intEvite var quando ele perde um tipo de interface deliberado. var list = new ArrayList<String>(); tipifica list como ArrayList<String>, não List<String> — aceitável localmente, mas se você pretendia programar para a interface, deixe isso claro.
Um exemplo prático que você pode executar
Este programa exercita var em todas as suas posições legais — valores simples, um mapa genérico, um loop for-each, um loop for indexado — e usa getClass().getSimpleName() para provar que os tipos inferidos em tempo de execução são exatamente o que os lados direitos implicaram.
O que extrair da execução:
greeting.getClass().getSimpleName()imprimeString, provando quevar greeting = "hello"produziu umaStringgenuína —varé inferência em tempo de compilação, e em tempo de execução o objeto é exatamente o que o literal implicou, nada dinâmico nisso.count + 1 = 43eprice * 2 = 19.98confirmam as regras de inferência numérica:42fezcountumint,9.99fezpriceumdouble. O tipo do literal — não sua intenção — decide, o que é a armadilha a lembrar quando você precisa de umlongoufloat.scores type = HashMapmostra quevarcapturou o tipo concreto do lado direitoHashMap, não a interfaceMap; o diamond<String, List<Integer>>no lado direito forneceu ao compilador tudo o que precisava, mesmo que o lado esquerdo dissesse apenasvar.total chars = 10vem defor (var name : names)ondenamefoi inferido comoString, entãoname.length()foi resolvido corretamente (3 + 3 + 4) —varfunciona em loops for-each, inferindo o tipo do elemento a partir do iterável.0..4 sum = 10vem defor (var i = 0; ...)ondeifoi inferido comointa partir do literal0; o loop indexado é um dos lugares mais limpos para usarvarporque o tipo é inconfundível.
Prática
Tópicos relacionados
- Variáveis Java — declarando e inicializando variáveis locais.
- Escopo de Variável — por que
varé intencionalmente limitado ao escopo local. - Generics — os nomes de tipo verbosos que
varé melhor em ocultar. - O loop
for— ondevar ievar entryficam legíveis.