Java Constantes (final)
Algumas informações de um programa não devem mudar nunca: o número de vagas de uma turma, a taxa de entrega da loja, o valor de π. Em Java, você marca essas variáveis com a palavra final, e o compilador passa a proibir qualquer tentativa de mudá-las. Nesta aula você vai criar constantes, ver o erro que o Java dá ao tentar alterá-las e aprender por que elas deixam o código mais fácil de entender e de manter.
- Como declarar uma variável
finale o que acontece ao tentar mudá-la. - Que uma variável
finalpode receber o valor depois, mas uma vez só. - As constantes da classe, com
static final, e a convençãoMAIÚSCULAS_COM_SUBLINHADO. - O que são "números mágicos" e por que trocá-los por constantes.
A ideia: escrito à caneta
Numa folha de cadastro, alguns campos você preenche a lápis, porque podem mudar (endereço, telefone), e outros à caneta, porque são definitivos (data de nascimento). Uma variável comum é o campo a lápis: você apaga e escreve de novo quantas vezes quiser. Uma variável final é o campo à caneta: depois de escrita, fica.
final quer dizer "final, definitivo": o valor que a variável recebe é o valor final dela. Uma variável que nunca muda é chamada de constante.
1. Variáveis final
Basta escrever final antes do tipo:
public class Main {
public static void main(String[] args) {
final int LIMITE_DE_ALUNOS = 30;
System.out.println("Vagas: " + LIMITE_DE_ALUNOS);
LIMITE_DE_ALUNOS = 35;
}
}
- final
- Modificador: "esta variável recebe valor uma única vez".
- int
- O tipo, como em qualquer variável.
- LIMITE_DE_ALUNOS
- O nome. Constantes costumam ser escritas em maiúsculas, com
_entre as palavras (veja a convenção adiante). - = 30;
- O valor, que nunca mais muda.
$ javac Main.java
Main.java:5: error: cannot assign a value to final variable LIMITE_DE_ALUNOS
LIMITE_DE_ALUNOS = 35;
^
1 error
"Não é possível atribuir um valor à variável final LIMITE_DE_ALUNOS". O erro acontece na compilação: o programa nem chega a rodar. É exatamente o que queremos: se alguém (inclusive você, daqui a seis meses) tentar mudar o limite por engano, o Java avisa na hora.
Sem a linha 5, o programa compila e mostra Vagas: 30. Ler uma constante é igual a ler qualquer variável; só atribuir é proibido.
Missão (provoque o erro): a taxa de entrega é uma constante. Acrescente, depois do println, uma linha que tenta mudar TAXA_DE_ENTREGA para 9.0 e veja o javac recusar.
public class Main {
public static void main(String[] args) {
final double TAXA_DE_ENTREGA = 7.50;
System.out.println("Entrega: " + TAXA_DE_ENTREGA);
}
}
public class Main {
public static void main(String[] args) {
final double TAXA_DE_ENTREGA = 7.50;
System.out.println("Entrega: " + TAXA_DE_ENTREGA);
TAXA_DE_ENTREGA = 9.0;
}
}
Atribuir depois, mas só uma vez
Uma variável final não precisa receber o valor na mesma linha. O que o Java garante é que ela recebe valor exatamente uma vez. Isso é útil quando o valor depende de uma decisão:
public class Main {
public static void main(String[] args) {
final double desconto;
int idade = 70;
if (idade >= 65) {
desconto = 0.5;
} else {
desconto = 0.0;
}
System.out.println(desconto);
}
}
$ javac Main.java && java Main 0.5
O if/else (assunto de if, else if, else) escolhe um dos dois caminhos; em qualquer deles, desconto recebe valor uma vez só, e o Java aceita. Repare que aqui o nome está em camelCase: desconto é final para não ser alterado por engano no resto do método, mas não é uma "constante do programa" como uma taxa fixa. Veja a diferença na convenção de nomes, mais adiante.
Se o Java perceber que a variável pode receber valor duas vezes, recusa:
public class Main {
public static void main(String[] args) {
final int x;
x = 1;
x = 2;
}
}
$ javac Main.java
Main.java:5: error: variable x might already have been assigned
x = 2;
^
1 error
"A variável x pode já ter sido atribuída". E, como qualquer variável local, uma final sem valor também não pode ser lida: final int TOTAL; seguido de println(TOTAL) dá variable TOTAL might not have been initialized, o mesmo erro visto em Variáveis.
final combina com var
final var cidade = "Recife"; é válido: o tipo é inferido (String) e a variável não pode mudar. Veja var.
2. Constantes da classe: static final
As constantes mais comuns não ficam dentro do main, e sim no topo da classe, fora de qualquer método. Assim, qualquer parte do programa pode usá-las, e quem abre o arquivo vê logo de cara os valores fixos. Para isso, elas levam também a palavra static:
public class Main {
static final double TAXA_DE_ENTREGA = 7.50;
static final int ESTOQUE_MINIMO = 5;
public static void main(String[] args) {
double compra = 42.0;
System.out.println("Total: " + (compra + TAXA_DE_ENTREGA));
System.out.println("Estoque mínimo: " + ESTOQUE_MINIMO);
System.out.println(Math.PI);
System.out.println(Integer.MAX_VALUE);
}
}
$ javac Main.java && java Main Total: 49.5 Estoque mínimo: 5 3.141592653589793 2147483647
- static
- "Da classe": existe uma só cópia, compartilhada por todo o programa. O
staticé assunto do Nível 2 (static); por enquanto, aceite a regra: constante no topo da classe levastatic final. - final
- Não pode mudar.
- double
- O tipo.
- TAXA_DE_ENTREGA
- O nome, em maiúsculas.
- = 7.50;
- O valor. Uma constante
static finalprecisa receber o valor aqui mesmo.
Os parênteses em "Total: " + (compra + TAXA_DE_ENTREGA) fazem a soma acontecer antes de juntar com o texto; sem eles, o Java colaria os dois números no texto e mostraria Total: 42.07.5 (o porquê está em Precedência).
Você já usou constantes do Java
Math.PI (π) e Integer.MAX_VALUE, que apareceram na saída acima, são constantes static final declaradas pelo próprio Java, nas classes Math e Integer. É por isso que se escreve o nome da classe, um ponto e o nome da constante. Dentro da classe Main, as suas constantes podem ser usadas só pelo nome (TAXA_DE_ENTREGA); de fora, seria Main.TAXA_DE_ENTREGA.
| Constante do Java | Valor | Aula |
|---|---|---|
Math.PI | 3.141592653589793 | Math |
Math.E | 2.718281828459045 (número de Euler) | Math |
Integer.MAX_VALUE | 2147483647 | int, long, short, byte |
Double.MAX_VALUE | 1.7976931348623157E308 | double e float |
A convenção de nomes
| O que é | Convenção | Exemplo |
|---|---|---|
Constante static final (valor fixo do programa) | MAIÚSCULAS_COM_SUBLINHADO (chamado SCREAMING_SNAKE_CASE, "cobra gritando") | TAXA_DE_ENTREGA, MAX_VALUE, PI |
Variável local final calculada no programa | camelCase, como qualquer variável | final double desconto |
| Variável comum | camelCase | totalDaCompra |
Quando você lê TAXA_DE_ENTREGA no meio de uma conta, as maiúsculas avisam: "isto é fixo, definido lá no topo". O Java não obriga a convenção (compila com qualquer nome), mas todo programador Java espera vê-la. Neste guia, constantes locais com valor fixo (como LIMITE_DE_ALUNOS no primeiro exemplo) também usam maiúsculas, para destacar que são valores de configuração.
3. Números mágicos
Olhe este cálculo de salário:
public class Main {
public static void main(String[] args) {
double salario = 3000;
double desconto = salario * 0.075;
double liquido = salario - desconto;
System.out.println(liquido);
}
}
O que é 0.075? Uma taxa de imposto? Uma comissão? Juros? Só quem escreveu sabe. Um número que aparece no código sem explicação se chama número mágico: funciona "por mágica", ninguém sabe de onde veio. Agora a mesma conta com uma constante:
public class Main {
static final double ALIQUOTA_INSS = 0.075;
public static void main(String[] args) {
double salario = 3000;
double desconto = salario * ALIQUOTA_INSS;
double liquido = salario - desconto;
System.out.println(liquido);
}
}
$ javac Main.java && java Main 2775.0
As duas versões dão exatamente o mesmo resultado (2775.0), mas a segunda tem três vantagens:
- Explica a si mesma:
salario * ALIQUOTA_INSSse lê como uma frase. - Muda num lugar só: se a alíquota mudar, você troca uma linha no topo. Com o número mágico, teria de caçar todos os
0.075do programa e torcer para não esquecer nenhum (nem trocar um0.075que era outra coisa). - Evita erro de digitação: errar
0.075como0.75compila e dá resultado errado em silêncio; errarALIQUOTA_INSSdácannot find symbolna hora.
0, 1, 2 em contas óbvias (contador + 1, n / 2 para metade, n % 2 para par ou ímpar) não precisam de constante. A pergunta é: "alguém que leia esta linha sabe de onde veio o número?". Se não sabe, crie uma constante.
Missão: o 0.10 é um número mágico (o desconto da loja). Crie no topo da classe a constante static final double DESCONTO = 0.10; e use-a na conta. A saída deve continuar Com desconto: 180.0.
public class Main {
public static void main(String[] args) {
double compra = 200.0;
double total = compra - compra * 0.10;
System.out.println("Com desconto: " + total);
}
}
public class Main {
static final double DESCONTO = 0.10;
public static void main(String[] args) {
double compra = 200.0;
double total = compra - compra * DESCONTO;
System.out.println("Com desconto: " + total);
}
}
Quando dá errado
$ javac Main.java
Main.java:5: error: cannot assign a value to final variable LIMITE_DE_ALUNOS
LIMITE_DE_ALUNOS = 35;
^
1 error
O que quer dizer: você tentou mudar uma variável final. Para as constantes da classe, a mensagem diz cannot assign a value to static final variable TAXA.
Como consertar: pergunte-se se o valor realmente deve mudar. Se sim, ele não é constante: tire o final (e use um nome em camelCase). Se não, a linha que tenta mudar é que está errada.
O que quer dizer: uma variável final declarada sem valor pode receber valor mais de uma vez no caminho do programa.
Como consertar: garanta uma única atribuição em cada caminho (como no exemplo do if/else) ou tire o final.
static final dentro do main: "illegal start of expression"
public class Main {
public static void main(String[] args) {
static final double TAXA = 7.50;
}
}
$ javac Main.java
Main.java:3: error: illegal start of expression
static final double TAXA = 7.50;
^
Main.java:5: error: class, interface, annotation type, enum, record, method or field expected
}
^
2 errors
O que quer dizer: "início de expressão ilegal". static só existe para coisas da classe; dentro de um método ele não faz sentido. O segundo erro é consequência do primeiro: o Java se perdeu e não reconheceu mais o fim das chaves.
Como consertar: dentro do método, use só final double TAXA = 7.50;. Para uma constante da classe, mova a linha para fora do main, logo abaixo de public class Main {.
$ javac Main.java
Main.java:2: error: variable MAX might not have been initialized
static final int MAX;
^
1 error
O que quer dizer: uma constante static final declarada sem valor nunca recebe um (não há if no topo da classe para decidir).
Como consertar: dê o valor na declaração: static final int MAX = 100;.
Referência
| Forma | Onde | Exemplo |
|---|---|---|
final tipo NOME = valor; | Dentro de um método | final int LIMITE_DE_ALUNOS = 30; |
final tipo nome; + uma atribuição | Dentro de um método | final double desconto; … desconto = 0.5; |
final var nome = valor; | Dentro de um método | final var cidade = "Recife"; |
static final tipo NOME = valor; | No topo da classe, fora dos métodos | static final double TAXA_DE_ENTREGA = 7.50; |
Classe.CONSTANTE | Usar a constante de outra classe | Math.PI, Integer.MAX_VALUE |
O final também se aplica a parâmetros, atributos, métodos e classes, cada um com um sentido próprio; isso é o assunto de final, no Nível 2.
Exercícios
1. Tornar constante
Complete para que VAGAS não possa mais ser alterada:
2. Constante da classe
Complete a declaração da constante no topo da classe:
3. Convenção de nome
Escreva o nome da constante "taxa de juros" seguindo a convenção do Java:
4. O que acontece ao compilar final int X = 1; X = 2;?
5. Qual é a principal vantagem de trocar 0.075 por ALIQUOTA_INSS?
6. Onde se declara uma constante static final?
Resumo
finalantes do tipo faz a variável receber valor uma vez só; mudar depois dácannot assign a value to final variable.- A atribuição pode vir depois da declaração, desde que aconteça exatamente uma vez em cada caminho.
- Constantes do programa ficam no topo da classe com
static finale nomes emMAIÚSCULAS_COM_SUBLINHADO. Math.PIeInteger.MAX_VALUEsão constantes do próprio Java.- Troque números mágicos por constantes com nome: o código se explica e muda num lugar só.