Falando como um desenvolvedor Java que faz todas as variáveis final
por padrão (e aprecia o fato de que o Eclipse pode fazer isso automaticamente), acho mais fácil raciocinar sobre o meu programa se as variáveis forem inicializadas uma vez e nunca forem alteradas novamente.
Por um lado, as variáveis não inicializadas não são mais motivo de preocupação, porque tentar usar uma variável final
antes de ser inicializada resultará em um erro de compilação. Isso é particularmente útil para lógicas condicionais aninhadas, nas quais desejo garantir que abordei todos os casos:
final int result;
if (/* something */) {
if (/* something else */) {
result = 1;
}
else if (/* some other thing */) {
result = 2;
}
}
else {
result = 3;
}
System.out.println(result);
Cobri todos os casos? (Dica: Não.) Com certeza, esse código nem vai compilar.
Mais uma coisa: em geral, sempre que você souber que algo é sempre verdade sobre uma variável, você deve tentar fazer com que sua linguagem seja aplicada. Fazemos isso todos os dias quando especificamos o tipo de uma variável, é claro: o idioma garantirá que os valores que não são desse tipo não possam ser armazenados nessa variável. Da mesma forma, se você sabe que uma variável não deve ser reatribuída porque ela já tem o valor que deve manter para todo o método, então você pode fazer com que o idioma imponha essa restrição declarando-a como final
.
Por fim, há a questão do hábito. Outros mencionaram que isso é um hábito (+1 para Jon para que ), mas deixe-me dizer algo sobre por que você iria querer esse hábito. Se você está declarando campos em sua classe e não variáveis locais em um método, é possível que vários encadeamentos acessem esses campos ao mesmo tempo. Existem algumas exceções obscuras, mas, em geral, se um campo for final, cada thread que usa sua classe verá o mesmo valor para a variável. Por outro lado, se um campo não for final e vários segmentos estiverem usando sua classe, você precisará se preocupar com a sincronização explícita usando synchronized
blocos e / ou classes de java.util.concurrent
. A sincronização é possível, mas a programação já é bastante difícil. ;-) Então, se você apenas declara tudo final
fora do hábito, muitos dos seus campos serão finais e você gastará o mínimo de tempo possível se preocupando com erros relacionados à sincronização e à simultaneidade .
Para saber mais sobre esse hábito, confira a dica "Minimizar a Mutabilidade" no Efetivo de Joshua Bloch Java .
Edit: @ Peter Taylor apontou que o exemplo acima também não compilará se a palavra-chave final
for removida, o que é completamente correto. Quando avisei a favor de manter todas as variáveis locais finais, é porque eu queria tornar exemplos como o seguinte:
int result = 0;
// OK, time to cover all the cases!
if (/* something */) {
if (/* something else */) {
result = 1;
}
else if (/* some other thing */) {
result = 2;
}
// Whoops, missed an "else" here. Too bad.
}
else {
result = 3;
}
System.out.println(result); // Works fine!
Usar uma nova variável em vez de reutilizar uma antiga é como posso dizer ao compilador que tenta cobrir todo o universo de possibilidades, e usar final
variables me obriga a usar uma nova variável em vez de reciclar uma antiga.
Outra reclamação válida sobre este exemplo é que você deve evitar lógica condicional aninhada complexa em primeiro lugar. Isso é verdade, é claro, precisamente porque é difícil garantir que você cobriu todos os casos da maneira pretendida. No entanto, às vezes a lógica complexa não pode ser evitada. Quando minha lógica é complexa, quero que minhas variáveis sejam tão simples quanto possível, o que posso conseguir certificando-me de que os valores de minhas variáveis nunca mudem depois de serem inicializados.