Java var Java 10+

Até aqui, toda variável começou com o tipo escrito por você: int idade = 29;. Desde o Java 10 existe um atalho: escrever var no lugar do tipo e deixar o compilador descobrir o tipo olhando o valor. Nesta aula você vai ver como o var funciona, onde ele pode e onde não pode ser usado, e quando ele deixa o código melhor ou pior.

Você vai aprender
  • O que é inferência de tipo e como o var funciona.
  • Que o var não cria uma variável "sem tipo": o tipo continua fixo.
  • Os lugares onde o var é proibido, com os erros reais do javac.
  • Boas práticas: quando o var ajuda e quando atrapalha.

A ideia: o Java lê a etiqueta por você

Quando você entrega um pacote nos Correios com uma garrafa dentro, o atendente não precisa que você escreva "GARRAFA" na caixa: ele vê a garrafa e já escolhe a embalagem certa. Com o var é assim: o compilador olha o valor do lado direito do = e escolhe o tipo da variável. Esse trabalho de "deduzir o tipo" se chama inferência de tipo (type inference).

public class Main {
    public static void main(String[] args) {
        var nome = "Ana";
        var idade = 29;
        var altura = 1.65;
        var inicial = 'A';
        var populacao = 8100000000L;
        var aprovada = true;
        System.out.println(nome + " " + idade + " " + altura + " " + inicial + " " + populacao + " " + aprovada);
    }
}
$ javac Main.java && java Main
Ana 29 1.65 A 8100000000 true
var
No lugar do tipo: "compilador, descubra o tipo pelo valor". Vem de variable, "variável".
idade
O nome, como sempre.
=
Recebe.
29
O valor. Como 29 é um literal int, a variável será int.

O tipo escolhido é sempre o tipo do valor, pelas regras que você viu nas aulas anteriores:

Com varÉ o mesmo quePor quê
var nome = "Ana";String nome = "Ana";Aspas duplas: String
var idade = 29;int idade = 29;Inteiro sem sufixo: int
var altura = 1.65;double altura = 1.65;Número com ponto: double
var inicial = 'A';char inicial = 'A';Aspas simples: char
var populacao = 8100000000L;long populacao = 8100000000L;Sufixo L: long
var aprovada = true;boolean aprovada = true;true/false: boolean

Vendo o tipo escolhido no JShell

No JShell, o comando /vars lista as variáveis com o tipo que o Java escolheu:

$ jshell
|  Welcome to JShell -- Version 25.0.4.1
|  For an introduction type: /help intro

jshell> var nome = "Ana"
nome ==> "Ana"

jshell> var idade = 29
idade ==> 29

jshell> var preco = 9.90
preco ==> 9.9

jshell> var grande = 10L
grande ==> 10

jshell> /vars
|    String nome = "Ana"
|    int idade = 29
|    double preco = 9.9
|    long grande = 10

jshell> /exit
|  Goodbye

var não é "sem tipo"

Quem conhece JavaScript ou Python pode achar que var cria uma variável que aceita qualquer coisa. Não cria. O tipo é decidido uma vez, na compilação, e nunca mais muda. O var só poupa você de escrever o tipo; o resultado é idêntico a ter escrito.

public class Main {
    public static void main(String[] args) {
        var idade = 29;
        idade = "trinta";
    }
}
$ javac Main.java
Main.java:4: error: incompatible types: String cannot be converted to int
        idade = "trinta";
                ^
1 error

Na linha 3, idade virou int para sempre. Na linha 4, guardar um texto nela dá o mesmo erro que daria com int idade. Do mesmo jeito, var x = 5; seguido de x = 2.5; dá possible lossy conversion from double to int.

Cuidado com o tipo que você "pediu" sem perceber

var preco = 10; cria um int, não um double. Se depois você fizer preco = preco / 4;, o resultado é a divisão inteira, 2, e não 2.5. Com var, o valor inicial decide tudo: se a variável vai guardar decimais, escreva var preco = 10.0; (ou simplesmente double preco = 10;).

public class Main {
    public static void main(String[] args) {
        int var = 5;
        System.out.println(var);
        var preco = 10;
        preco = preco / 4;
        System.out.println(preco);
        var preco2 = 10.0;
        System.out.println(preco2 / 4);
    }
}
$ javac Main.java && java Main
5                  « int var = 5; (curiosidade explicada adiante)
2                  « var preco = 10; preco = preco / 4;
2.5                « var preco2 = 10.0; preco2 / 4

Missão: reescreva as quatro declarações usando var no lugar do tipo. A saída deve continuar Banana 6 4.5 true.

public class Main {
    public static void main(String[] args) {
        String fruta = "Banana";
        int quantidade = 6;
        double preco = 4.5;
        boolean maduras = true;
        System.out.println(fruta + " " + quantidade + " " + preco + " " + maduras);
    }
}
public class Main {
    public static void main(String[] args) {
        var fruta = "Banana";
        var quantidade = 6;
        var preco = 4.5;
        var maduras = true;
        System.out.println(fruta + " " + quantidade + " " + preco + " " + maduras);
    }
}

Onde pode e onde não pode

O nome oficial do recurso é "inferência de tipo de variáveis locais". Ou seja: o var só vale para variáveis declaradas dentro de um método (como o main), e só quando há um valor do qual deduzir o tipo.

SituaçãoPode?Erro real
Variável local com valor: var n = 10;Sim
Variável do for e do for-each: for (var i = 0; …)Sim
Sem valor: var total;Nãocannot use 'var' on variable without initializer
Valor null: var cliente = null;Nãovariable initializer is 'null'
Várias numa linha: var a = 1, b = 2;Não'var' is not allowed in a compound declaration
Array com chaves: var notas = {7, 8, 9};Nãoarray initializer needs an explicit target-type
Atributo (fora do método, dentro da classe)Não'var' is not allowed here
Parâmetro de método: void saudar(var nome)Não'var' is not allowed here

Todos esses erros têm a mesma raiz: o compilador precisa de um valor com tipo claro, na mesma linha, para escolher o tipo. Veja cada um em Quando dá errado.

Curiosidade: var não é palavra reservada

Para não quebrar programas antigos que tinham variáveis chamadas var, o Java tornou var um "nome de tipo reservado", e não uma palavra-chave como int. Por isso int var = 5; ainda compila (foi o 5 da saída acima). Funciona, mas não faça isso: confunde qualquer leitor.

E em versões antigas do Java?

O var surgiu no Java 10 (2018). Em projetos presos ao Java 8, ele não existe: o javac procura uma classe chamada var e responde cannot find symbol. Nesses projetos, escreva o tipo. O guia ensina o Java 25, em que o var funciona normalmente (veja Versões e LTS).


Boas práticas: quando usar

O var é uma ferramenta de legibilidade. A regra de ouro: use quando o tipo fica óbvio lendo a linha; evite quando o leitor teria de adivinhar.

Bom usoPor quê
var leitor = new Scanner(System.in);O tipo já aparece à direita (new Scanner); escrever Scanner leitor = new Scanner(…) repete a palavra.
var nome = "Ana";Aspas = texto. Ninguém tem dúvida.
var mapa = new HashMap<String, List<Integer>>();Tipos longos de coleções: o var evita escrever duas vezes.
EvitePor quêPrefira
var resultado = calcular();Que tipo calcular() devolve? Só abrindo outro arquivo para saber.double resultado = calcular();
var preco = 10;Vira int; se a intenção era decimal, é um erro escondido.double preco = 10;
var x = 0L;O L é fácil de não ver.long x = 0;
Dica para iniciantes

Enquanto você está aprendendo, escreva os tipos. Ver int, double e String em cada linha ajuda a fixar o que cada variável guarda e a entender os erros do javac. Use o var quando o tipo for longo e repetido, como nos exemplos com new.


Quando dá errado

var sem valor: "cannot infer type for local variable total"
public class Main {
    public static void main(String[] args) {
        var total;
        total = 10;
    }
}
$ javac Main.java
Main.java:3: error: cannot infer type for local variable total
        var total;
            ^
  (cannot use 'var' on variable without initializer)
1 error

O que quer dizer: "não consigo inferir o tipo da variável local total (não se pode usar var numa variável sem inicializador)". O compilador decide o tipo na linha da declaração; ele não olha as linhas seguintes.

Como consertar: dê o valor na mesma linha (var total = 10;) ou escreva o tipo (int total;).

var com null: "variable initializer is 'null'"
public class Main {
    public static void main(String[] args) {
        var cliente = null;
    }
}
$ javac Main.java
Main.java:3: error: cannot infer type for local variable cliente
        var cliente = null;
            ^
  (variable initializer is 'null')
1 error

O que quer dizer: null ("nenhum objeto") serve para qualquer tipo de referência, então não diz qual tipo a variável deve ter.

Como consertar: escreva o tipo: String cliente = null;.

var fora de um método: "'var' is not allowed here"
public class Main {
    var contador = 0;

    public static void main(String[] args) {
        System.out.println("oi");
    }
}
$ javac Main.java
Main.java:2: error: 'var' is not allowed here
    var contador = 0;
    ^
1 error

O que quer dizer: "var não é permitido aqui". A linha 2 está dentro da classe, mas fora de qualquer método: é um atributo (Atributos), e atributos sempre exigem o tipo escrito. A mesma mensagem aparece em parâmetros de métodos: static void saudar(var nome).

Como consertar: escreva o tipo: int contador = 0;.

"'var' is not allowed in a compound declaration"
$ javac Main.java
Main.java:3: error: 'var' is not allowed in a compound declaration
        var a = 1, b = 2;
        ^
1 error

O que quer dizer: "declaração composta" é a de várias variáveis na mesma linha, separadas por vírgula. Com var, é uma por linha.

Como consertar: var a = 1; e var b = 2; em linhas separadas, ou int a = 1, b = 2;.

"array initializer needs an explicit target-type"
$ javac Main.java
Main.java:3: error: cannot infer type for local variable notas
        var notas = {7, 8, 9};
            ^
  (array initializer needs an explicit target-type)
1 error

O que quer dizer: as chaves {7, 8, 9} criam um array, mas só quando o tipo está escrito à esquerda. Sozinhas, não dizem se é um array de int, de long ou de double.

Como consertar: int[] notas = {7, 8, 9}; ou var notas = new int[] {7, 8, 9};.

Missão (provoque o erro): altere a linha do var para que o javac responda cannot infer type for local variable. Há duas formas vistas nesta aula; qualquer uma vale.

public class Main {
    public static void main(String[] args) {
        var cidade = "Recife";
        System.out.println(cidade);
    }
}
public class Main {
    public static void main(String[] args) {
        var cidade = null;
        System.out.println(cidade);
    }
}

Referência

FormaTipo inferidoObservação
var n = 10;intInteiro sem sufixo
var n = 10L;long
var x = 10.0;doublevar x = 10; seria int
var f = 1.5f;float
var c = 'A';char
var s = "Ana";String
var b = true;boolean
var sc = new Scanner(System.in);ScannerO tipo que vem depois de new
var x; / var x = null;—Não compila
var em atributo ou parâmetro—Não compila

Exercícios

1. Inferência

Complete com a palavra que deixa o compilador descobrir o tipo:

cidade = "Recife";

2. Qual o tipo?

Em var media = 7.5;, que tipo a variável media recebe?

3. Forçar long

Complete para contador ser um long:

var contador = 0;

4. Qual destas linhas compila?

5. Depois de var idade = 29;, o que acontece com idade = "trinta";?

6. Desde qual versão do Java existe o var?


Resumo

  • var (Java 10+) deixa o compilador deduzir o tipo pelo valor: var idade = 29; é exatamente int idade = 29;.
  • O tipo continua fixo: var não cria variável "sem tipo".
  • Só para variáveis locais (dentro de métodos), com valor na mesma linha. Não vale sem valor, com null, em várias numa linha, com {…} de array, em atributos nem em parâmetros.
  • Use quando o tipo é óbvio na linha (var leitor = new Scanner(System.in);); escreva o tipo quando ele ajuda a entender.
  • Cuidado: var preco = 10; é int.