W3docs

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 String

var é 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çãovar permitido?Motivo
Variável local com inicializadorSimO inicializador fornece o tipo
Índice/elemento em loops forSimA expressão do loop fornece o tipo
Variável em try-with-resourcesSimA expressão do recurso fornece o tipo
Variável local sem inicializadorNãoNada a inferir
Campos / variáveis de instânciaNãoA inferência é somente local por design
Parâmetros de métodoNãoChamadores, não inicializadores, fornecem valores
Tipos de retorno de métodoNãoMesmo motivo que parâmetros
Inicializado apenas com nullNãonull 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 parameters

O 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 int

Cuidado 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 int

Evite 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.

java— editable, runs on the server

O que extrair da execução:

  • greeting.getClass().getSimpleName() imprime String, provando que var greeting = "hello" produziu uma String genuí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 = 43 e price * 2 = 19.98 confirmam as regras de inferência numérica: 42 fez count um int, 9.99 fez price um double. O tipo do literal — não sua intenção — decide, o que é a armadilha a lembrar quando você precisa de um long ou float.
  • scores type = HashMap mostra que var capturou o tipo concreto do lado direito HashMap, não a interface Map; o diamond <String, List<Integer>> no lado direito forneceu ao compilador tudo o que precisava, mesmo que o lado esquerdo dissesse apenas var.
  • total chars = 10 vem de for (var name : names) onde name foi inferido como String, então name.length() foi resolvido corretamente (3 + 3 + 4) — var funciona em loops for-each, inferindo o tipo do elemento a partir do iterável.
  • 0..4 sum = 10 vem de for (var i = 0; ...) onde i foi inferido como int a partir do literal 0; o loop indexado é um dos lugares mais limpos para usar var porque o tipo é inconfundível.

Prática

Prática
Em qual dessas declarações 'var' é legal e infere o tipo que o comentário afirma?
Em qual dessas declarações 'var' é legal e infere o tipo que o comentário afirma?

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 — onde var i e var entry ficam legíveis.
Was this page helpful?