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.
- O que é inferência de tipo e como o
varfunciona. - Que o
varnão cria uma variável "sem tipo": o tipo continua fixo. - Os lugares onde o
varé proibido, com os erros reais dojavac. - Boas práticas: quando o
varajuda 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 literalint, 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 que | Por 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.
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ção | Pode? | 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ão | cannot use 'var' on variable without initializer |
Valor null: var cliente = null; | Não | variable 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ão | array 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.
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 uso | Por 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. |
| Evite | Por 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; |
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;.
$ 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;.
$ 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
| Forma | Tipo inferido | Observação |
|---|---|---|
var n = 10; | int | Inteiro sem sufixo |
var n = 10L; | long | |
var x = 10.0; | double | var 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); | Scanner | O 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:
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:
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;é exatamenteint idade = 29;.- O tipo continua fixo:
varnã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.