Eu considero final
nos parâmetros do método e variáveis locais como ruído de código. As declarações de método Java podem ser bastante longas (especialmente com genéricos) - não há mais necessidade de fazê-las.
Se os testes de unidade forem escritos corretamente, a atribuição de parâmetros "prejudiciais" será detectada, portanto nunca deve ser realmente um problema. A clareza visual é mais importante do que evitar um possível bug que não é detectado porque seus testes de unidade têm cobertura insuficiente.
Ferramentas como FindBugs e CheckStyle que podem ser configuradas para interromper a compilação se a atribuição for feita a parâmetros ou variáveis locais, se você se importar profundamente com essas coisas.
Obviamente, se você precisar finalizá-las, por exemplo, porque você está usando o valor em uma classe anônima, então não há problema - essa é a solução mais simples e limpa.
Além do efeito óbvio de adicionar palavras-chave extras aos seus parâmetros e, assim, camuflar o IMHO, adicionar parâmetros finais ao método geralmente pode tornar o código no corpo do método menos legível, o que torna o código pior - para ser "bom", código deve ser o mais legível e simples possível. Para um exemplo artificial, digamos que eu tenha um método que precise trabalhar com maiúsculas e minúsculas.
Sem final
:
public void doSomething(String input) {
input = input.toLowerCase();
// do a few things with input
}
Simples. Limpar \ limpo. Todo mundo sabe o que está acontecendo.
Agora com 'final', opção 1:
public void doSomething(final String input) {
final String lowercaseInput = input.toLowerCase();
// do a few things with lowercaseInput
}
Embora a final
definição dos parâmetros impeça o codificador de adicionar o código mais longe de pensar que ele está trabalhando com o valor original, há um risco igual que o código mais abaixo possa usar em input
vez de lowercaseInput
, o que não deveria e o que não pode ser protegido, porque você pode ' t levá-lo fora do escopo (ou mesmo atribuir null
para input
se isso fosse mesmo ajudar de qualquer maneira).
Com 'final', opção 2:
public void doSomething(final String input) {
// do a few things with input.toLowerCase()
}
Agora, acabamos de criar ainda mais ruído no código e introduzimos um impacto no desempenho de ter que invocar toLowerCase()
n vezes.
Com 'final', opção 3:
public void doSomething(final String input) {
doSomethingPrivate(input.toLowerCase());
}
/** @throws IllegalArgumentException if input not all lower case */
private void doSomethingPrivate(final String input) {
if (!input.equals(input.toLowerCase())) {
throw new IllegalArgumentException("input not lowercase");
}
// do a few things with input
}
Fale sobre o ruído do código. Isto é um acidente de trem. Temos um novo método, um bloco de exceção necessário, porque outro código pode invocá-lo incorretamente. Mais testes de unidade para cobrir a exceção. Tudo para evitar uma linha simples, e IMHO, preferível e inofensiva.
Há também a questão de que os métodos não devem ser tão longos que você não pode visualmente visualizá-lo facilmente e saber rapidamente que uma atribuição ao parâmetro ocorreu.
Eu acho que é uma boa prática / estilo que, se você atribuir a um parâmetro, faça isso todo o começo do método, de preferência na primeira linha ou logo após a verificação básica de entrada, substituindo -o efetivamente por todo o método, o que tem um efeito consistente dentro do método método. Os leitores sabem que qualquer tarefa é óbvia (próximo à declaração da assinatura) e em um local consistente, o que atenua bastante o problema que a adição final está tentando evitar. Na verdade, raramente atribuo a parâmetros, mas, se o faço, sempre o faço no topo de um método.
Observe também que final
, na verdade, não o protege como pode parecer à primeira vista:
public void foo(final Date date) {
date.setTime(0);
// code that uses date
}
final
não o protege completamente, a menos que o tipo de parâmetro seja primitivo ou imutável.