Usando o modificador "final" sempre que aplicável em Java [fechado]

194

Em Java, existe a prática de declarar todas as variáveis ​​(local ou classe), parâmetro final, se realmente forem.

Embora isso torne o código muito mais detalhado, isso ajuda na leitura / compreensão fácil do código e também evita erros, pois a intenção é claramente marcada.

O que você pensa sobre isso e o que você segue?

surajs
fonte
13
Isso pode levar a uma discussão religiosa. Algumas pessoas gostam, outras pessoas odeiam. Gosto de campos finais, mas não de variáveis ​​locais finais, a menos que precisem ser, sem ter certeza de que é totalmente racional. Não tenho certeza se qualquer voto negativo seria. Eu concordo com Alex Miller. ;)
Peter Lawrey
Eu posso entender se as pessoas não gostam de ter seu código cheio de finais. Mas este é um problema que um bom editor poderia resolver: bugs.eclipse.org/bugs/show_bug.cgi?id=409379
oberlies

Respostas:

184

Eu acho que tudo tem a ver com um bom estilo de codificação. É claro que você pode escrever programas bons e robustos sem usar muitos finalmodificadores em qualquer lugar, mas quando pensa nisso ...

Adicionar finaltodas as coisas que não devem mudar simplesmente reduz as possibilidades de você (ou o próximo programador, trabalhando no seu código) interpretar ou usar mal o processo de pensamento que resultou no seu código. Pelo menos, deve tocar alguns sinos quando eles agora querem mudar sua coisa imutável.

No começo, parece meio estranho ver muitas finalpalavras-chave em seu código, mas logo você irá parar de perceber a palavra em si e simplesmente pensará: "aquilo que nunca mudará a partir deste ponto" on (você pode tirar isso de mim ;-)

Eu acho que é uma boa prática. Não o uso o tempo todo, mas quando eu puder e fizer sentido rotular algo, finaleu o farei.

Johan Pelgrim
fonte
16
Em vez de usar a palavra-chave final para parâmetros, você pode usar a ferramenta de análise estática, como FindBugs, para exigir que as variáveis ​​de parâmetro não sejam reatribuídas. Dessa forma, você remove a carga sintática e pode aplicá-la através de uma verificação FindBugs na sua ferramenta de integração contínua.
Timo Westkämper
20
@Timo Isso funcionaria, mas tem a desvantagem de que isso precisa ser verificado após o check-in e não enquanto o desenvolvedor estiver trabalhando no código. Pois final, obviamente, o compilador não permitirá que você continue se cometer um erro.
Mike
9
O Eclipse tem uma opção para adicionar finalsempre que possível (variáveis ​​que não mudam) sempre que você salvar.
Bert F
22
+1 eu amo final. Não faz diferença 98% do tempo, é um pequeno inconveniente% 1 do tempo, mas o 1% do tempo me impede de fazer algo estúpido ou involuntário que vale a pena.
Bert F
14
@BertF Sim, eu sempre deixo o Eclipse adicionar finalmodificadores em todos os lugares quando eu "Limpo ..." meu código. Em todos os lugares significa até nos blocos catch () e, como dito, você não os notará depois de um tempo. Obviamente, se eu criar um idioma, esse finalseria o padrão e a varou modifiableseria a palavra-chave opcional.
Maarten Bodewes
191

Obcecado por:

  • Campos finais - A marcação de campos como final os força a serem definidos no final da construção, tornando imutável essa referência de campo. Isso permite a publicação segura de campos e pode evitar a necessidade de sincronização em leituras posteriores. (Observe que, para uma referência a objeto, apenas a referência de campo é imutável - as coisas a que a referência a objeto se refere ainda podem mudar e isso afeta a imutabilidade.)
  • Campos estáticos finais - Embora eu use enumerações agora para muitos dos casos em que costumava usar campos finais estáticos.

Considere, mas use criteriosamente:

  • Classes finais - O design da estrutura / API é o único caso em que eu o considero.
  • Métodos finais - basicamente o mesmo que as aulas finais. Se você estiver usando padrões de métodos de modelo, como coisas loucas e marcando final, provavelmente estará confiando demais na herança e não na delegação.

Ignore, a menos que sinta anal:

  • Parâmetros de método e variáveis ​​locais - RARAMENTE faço isso em grande parte porque sou preguiçoso e acho que isso atrapalha o código. Admito plenamente que a marcação de parâmetros e variáveis ​​locais que não vou modificar é "mais correta". Eu gostaria que fosse o padrão. Mas não é e acho o código mais difícil de entender com as finais por todo o lado. Se eu estiver no código de outra pessoa, não vou retirá-lo, mas se estiver escrevendo um novo código, não o colocarei. Uma exceção é o caso em que você precisa marcar algo final para poder acessar de dentro de uma classe interna anônima.
Alex Miller
fonte
8
melhor resposta ainda em várias perguntas sobre este tópico final
Gabriel Ščerbák 18/11/2010
4
Na maioria das vezes, quando vejo a variável local sem a palavra final à sua frente e ela não pode ser usada, me diz que provavelmente devo extrair algum código em um novo método que retorne o valor desejado que tornarei final. Os casos em que isso não é aplicável é quando eu uso alguns fluxos ou que precisa tentar capturá-lo.
Nyxz 07/11
32

Você realmente precisa entender o uso completo da palavra-chave final antes de usá-la. Pode aplicar-se ae tem efeitos diferentes em variáveis, campos, métodos e classes

Eu recomendo verificar o artigo vinculado abaixo para obter mais detalhes.

Palavra final Na palavra-chave final

HowardSP
fonte
29

O finalmodificador, especialmente para variáveis, é um meio de fazer com que o compilador aplique uma convenção geralmente sensata: verifique se uma variável (local ou instância) é atribuída exatamente uma vez (nem mais nem menos). Ao garantir que uma variável seja definitivamente atribuída antes de ser usada, você pode evitar casos comuns de a NullPointerException:

final FileInputStream in;
if(test)
  in = new FileInputStream("foo.txt");
else
  System.out.println("test failed");
in.read(); // Compiler error because variable 'in' might be unassigned

Ao impedir que uma variável seja atribuída mais de uma vez, você desencoraja o escopo do overbroad. Em vez disso:

 String msg = null;
 for(int i = 0; i < 10; i++) {
     msg = "We are at position " + i;
     System.out.println(msg);
 }
 msg = null;

Você é incentivado a usar isso:

 for(int i = 0; i < 10; i++) {
     final String msg = "We are at position " + i;
     System.out.println(msg);
 }

Alguns links:

Bruno De Fraine
fonte
1
+1 para desencorajar o argumento de escopo overbroad
Tom Tresansky
Em resumo, você pode basicamente usar finalpara tornar o Java mais baseado em expressões .
Adam Gent
Na verdade "Ao garantir que uma variável seja definitivamente atribuída antes de ser usada, você pode evitar casos comuns de uma NullPointerException:" isso não está correto, 'final' não faz diferença aqui, o compilador sabe e reclama sobre a variável 'in' possivelmente sendo nulo no exemplo fornecido.
nyholku 29/01/19
@nyholku Você está certo, em Java as variáveis ​​locais devem ser definitivamente atribuídas antes do uso, também sem final. Mas para os campos você precisa da final. Em um exemplo um pouco mais complexo, em que "in" é uma variável de instância de uma classe e o if / else está no construtor, você precisa da final para sinalizar o problema. Além disso, também para variáveis ​​locais, há algum valor na IMO final, pois impede que o programador desleixado 'conserte' o erro de compilação sobre "in" ser possivelmente não atribuído, adicionando "= null" à sua declaração. Em outras palavras, variáveis ​​finais reduzem o uso de null, na minha experiência.
Bruno De Fraine
20

Sou bastante dogmático sobre declarar todas as variáveis ​​possíveis final. Isso inclui parâmetros de método, variáveis ​​locais e, raramente, campos de objetos de valor. Eu tenho três razões principais para declarar variáveis ​​finais em todos os lugares:

  1. Declarando Intenção: Ao declarar uma variável final, afirmo que essa variável deve ser gravada apenas uma vez. É uma dica sutil para outros desenvolvedores e uma grande dica para o compilador.
  2. Aplicação de variáveis ​​de uso único: Acredito na ideia de que cada variável deve ter apenas um propósito na vida. Ao dar a cada variável apenas um propósito, você reduz o tempo necessário para agregar o objetivo dessa variável específica durante a depuração.
  3. Permite otimização: Eu sei que o compilador costumava ter truques de aprimoramento de desempenho, que dependiam especificamente da imutabilidade de uma referência variável. Eu gosto de pensar que alguns desses truques antigos de desempenho (ou novos) serão usados ​​pelo compilador.

No entanto, acho que as classes e métodos finais não são tão úteis quanto as referências finais às variáveis. A finalpalavra-chave, quando usada com essas declarações, simplesmente fornece obstáculos para testes automatizados e o uso do seu código de maneiras que você nunca poderia ter previsto.

Ryan Ransford
fonte
2
finalvariáveis ​​e parâmetros de método não têm impacto no desempenho, pois não é expresso no bytecode.
Steve Kuo
variável: = latin: varius> vários> modificável
dieter
17

Java efetivo possui um item que diz "Favorecer objetos imutáveis". Declarar os campos como finais ajuda a dar alguns pequenos passos nesse sentido, mas é claro que há muito mais em objetos realmente imutáveis ​​do que isso.

Se você souber que os objetos são imutáveis, eles podem ser compartilhados para leitura entre muitos encadeamentos / clientes sem preocupações com a sincronização, e é mais fácil argumentar sobre como o programa é executado.

Lars Westergren
fonte
15
cuidadoso - final e imutável são conceitos completamente diferentes. É fácil ter um objeto mutável final - final é sobre a referência, mutabilidade é sobre a instância do objeto. Pessoa final olaf = new Person (); olaf.setName ("Olaf");
Olaf Kock
1
Exatamente - objetos imutáveis ​​são uma coisa, referências imutáveis ​​(ou seja, finais) são algo totalmente diferente.
SCdF 27/09/08
2
Mas eles estão intimamente relacionados, pois você pode declarar o campo Person.name como final (e declarar a classe final e ...) tornar o objeto Person final. Contudo, não é tão fácil como apenas fazendo "Pessoa final" ...
Sebastian Ganslandt
Isso também é irrelevante para variáveis ​​locais (os parâmetros são locais). Ele se aplica apenas a variáveis ​​de classe e, portanto, aborda apenas parcialmente a questão.
Robin
12

Eu nunca estive em uma situação em que ter uma palavra-chave final em uma variável me impedisse de cometer um erro, então, no momento, acho que é uma enorme perda de tempo.

A menos que haja uma razão real para fazê-lo (como você deseja fazer um argumento específico sobre a variável final), prefiro não fazê-lo, pois acho que isso torna o código menos legível.

Se, no entanto, você não achar que isso torna o código mais difícil de ler ou mais longo para escrever, faça o que for necessário.

Edit: Como um esclarecimento (e uma tentativa de recuperar os votos negativos), não estou dizendo que não marque as constantes como finais, estou dizendo que não faça coisas como:

public String doSomething() {
  final String first = someReallyComplicatedExpressionToGetTheString();
  final String second = anotherReallyComplicatedExpressionToGetAnother();

  return first+second;
}

Isso apenas torna o código (na minha opinião) mais difícil de ler.

Também vale lembrar que tudo o que faz é impedir que você reatribua uma variável, não a torna imutável ou algo assim.

SCdF
fonte
1
Revirando a afirmação: estive em muitas situações em que o não uso final(e objetos imutáveis ​​em geral) teve um fator contribuinte significativo para o número e o impacto dos bugs.
Chris Vest
3
Sou a favor de objetos imutáveis, nunca estive em uma situação em que marcar uma final de objeto imutável me ajudou.
SCdF 26/09/08
2
Concordo que não devemos usar final em variáveis ​​locais e parâmetros de método, isso torna o código muito menos legível.
Rmaruszewski
@ChrisVest, talvez suas funções sejam muito longas
Pacerier
8

Final sempre deve ser usado para constantes. É até útil para variáveis ​​de vida curta (dentro de um único método) quando as regras para definir a variável são complicadas.

Por exemplo:

final int foo;
if (a)
    foo = 1;
else if (b)
    foo = 2;
else if (c)
    foo = 3;
if (d)        // Compile error:  forgot the 'else'
    foo = 4;
else
    foo = -1;
David Leppik
fonte
6

Parece um dos maiores argumentos contra o uso da palavra-chave final: "é desnecessário" e "desperdiça espaço".

Se reconhecermos os muitos benefícios de "final", como apontado por muitas ótimas postagens aqui, embora admita que seja necessário mais digitação e espaço, eu argumentaria que o Java deveria ter tornado as variáveis ​​"finais" por padrão e requer que as coisas sejam marcadas " mutável "se o codificador quiser.

RAIO
fonte
1
Downvoters, gostaria de explicar?
RAY
Parece estranho chamá-lo de variável, não é?
Alan
2
Existem milhares de palavras em inglês para escolher.
RAY
Isto é perfeito. Não há argumentos contra, finalalém de "muito tempo para escrever", "torna o código mais desajeitado". Existem vários argumentos para final. E podemos adicioná-lo automaticamente ao salvar, se você não quiser digitá-lo manualmente.
Dmitriy Popov
5

Eu uso finalo tempo todo para atributos de objetos.

A finalpalavra-chave possui semântica de visibilidade quando usada em atributos de objeto. Basicamente, a configuração do valor de um atributo de objeto final acontece antes do retorno do construtor. Isso significa que, desde que você não deixe a thisreferência escapar do construtor e use finalpara todos seus atributos, seu objeto (na semântica do Java 5) é garantido para ser construído corretamente e, como também é imutável, pode ser publicado com segurança para outros segmentos.

Objetos imutáveis ​​não são apenas segurança de threads. Eles também facilitam muito o raciocínio sobre as transições de estado no seu programa, porque o espaço do que pode mudar é deliberado e, se usado de forma consistente, completamente limitado apenas às coisas que devem mudar.

Às vezes, também finalizo os métodos, mas não com tanta frequência. Raramente faço aulas finais. Geralmente faço isso porque tenho pouca necessidade. Geralmente não uso muito a herança. Prefiro usar interfaces e composição de objetos - isso também se presta a um design que eu acho que é mais fácil de testar. Quando você codifica para interfaces em vez de classes concretas, não precisa usar herança ao testar, como é, com estruturas como jMock, muito mais fácil criar objetos simulados com interfaces do que com classes concretas.

Acho que devo finalizar a maioria das minhas aulas, mas ainda não entrei no habbit.

Chris Vest
fonte
4

Eu tenho que ler muito código para o meu trabalho. A falta de variáveis ​​finais na instância é uma das principais coisas para me incomodar e dificulta a compreensão desnecessária do código. Para meu dinheiro, as variáveis ​​locais finais causam mais confusão do que clareza. O idioma deveria ter sido projetado para tornar esse padrão, mas temos que conviver com o erro. Às vezes, é útil principalmente em loops e designações definidas com uma árvore if-else, mas geralmente indica que seu método é muito complicado.

Tom Hawtin - linha de orientação
fonte
Por que não usar uma ferramenta que reduz a confusão?
Pacerier
@Pacerier Como em um editor que deturpa o código?
Tom Hawtin -
3

obviamente, o final deve ser usado em constantes e para reforçar a imutabilidade, mas há outro uso importante nos métodos.

Java efetivo tem um item inteiro sobre isso (Item 15), apontando as armadilhas da herança não intencional. Efetivamente, se você não projetou e documentou sua classe para herança, herdá-la pode causar problemas inesperados (o item é um bom exemplo). A recomendação, portanto, é que você use final em qualquer classe e / ou método do qual não se destina a ser herdado.

Isso pode parecer draconiano, mas faz sentido. Se você estiver escrevendo uma biblioteca de classes para uso de outras pessoas, não as desejará herdar de coisas que não foram projetadas para ela - você estará preso a uma implementação específica da classe para compatibilidade retroativa. Se você estiver codificando em uma equipe, não há nada para impedir outro membro da equipe de remover a final, se for necessário. Mas a palavra-chave os faz pensar sobre o que estão fazendo e os avisa que a classe da qual eles estão herdando não foi projetada para isso, portanto, eles devem ter cuidado extra.

DJClayworth
fonte
3

Outra ressalva é que muitas pessoas confundem final com o significado de que o conteúdo da variável de instância não pode mudar, e que a referência não pode mudar.

Uri
fonte
E este post é uma grande prova disso.
InigoD 03/03
2

Mesmo para variáveis ​​locais, saber que é declarado final significa que não preciso me preocupar com a alteração da referência posteriormente. Isso significa que, ao depurar e vejo essa variável mais tarde, estou confiante de que está se referindo ao mesmo objeto. É uma coisa a menos com a qual preciso me preocupar ao procurar um bug. Um bônus é que, se 99% das variáveis ​​forem declaradas finais, as poucas variáveis ​​que realmente são variáveis ​​se destacam melhor. Além disso, a final permite que o compilador encontre mais alguns erros estúpidos possíveis que, de outra forma, poderiam passar despercebidos.

Diastrofismo
fonte
2

Escolhendo digitar final para cada parâmetro em cada método produzirá muita irritação, tanto para codificadores quanto para leitores de código.

Depois que a irritação for além do razoável, mude para Scala, onde os argumentos são finais por padrão.

Ou você sempre pode usar ferramentas de estilo de código que farão isso automaticamente para você. Todos os IDEs os implementam ou são como plugins.

Oleg Mikheev
fonte
1

Final quando usado com variáveis ​​em Java fornece um substituto para constante em C ++. Portanto, quando final e estático é usado para uma variável, torna-se imutável. Ao mesmo tempo, os programadores C ++ migrados ficam muito felizes ;-)

Quando usado com variáveis ​​de referência, não permite que você refira a referência ao objeto, embora o objeto possa ser manipulado.

Quando final é usado com um método, ele não permite que o método seja substituído pelas subclasses.

Uma vez que o uso é muito claro, deve ser usado com cuidado. Depende principalmente do design, pois o uso final do método não ajudaria o polimorfismo.

Só se deve usá-lo para variáveis ​​quando você tiver certeza absoluta de que o valor da variável nunca será alterado. Também verifique se você segue a convenção de codificação incentivada pelo SUN.for, por exemplo: final int COLOR_RED = 1; (Maiúsculas separadas por sublinhado)

Com uma variável de referência, use-a apenas quando precisarmos de uma referência imutável para um objeto específico.

Em relação à parte de legibilidade, verifique se os comentários desempenham um papel muito importante ao usar o modificador final.

Onipotente
fonte
Alguém pode me dizer por que isso é votado para baixo ??? Apenas curioso ..
Onipotente
Provavelmente porque você declara que APENAS deve torná-lo final quando XYZ. Outros consideram melhor prática tornar TUDO final, a menos que seja necessário fazê-lo de outra forma.
Outlaw Programmer
1
Não substitui a palavra-chave const C ++. -1.
ATM
1

Eu nunca os uso em variáveis ​​locais, há pouco sentido para a verbosidade adicional. Mesmo que você ache que a variável não deve ser reatribuída, isso fará pouca diferença para a próxima pessoa que alterar o código que pensa de outra forma e, como o código está sendo alterado, qualquer finalidade original para torná-lo final pode não ser mais válida. Se é apenas por clareza, acredito que falha devido aos efeitos negativos da verbosidade.

O mesmo se aplica às variáveis-membro, pois elas fornecem pouco benefício, exceto no caso de constantes.

Também não afeta a imutabilidade, pois o melhor indicador de que algo é imutável é que ele está documentado como tal e / ou não possui métodos que possam alterar o objeto (isso, juntamente com a finalização da aula, é a única maneira de garantir que é imutável).

Mas ei, essa é apenas a minha opinião :-)

Robin
fonte
1

Eu configurei o Eclipse para adicionar final em todos os campos e atributos que não são modificados. Isso funciona muito bem usando o Eclipse "salvar ações", que adiciona esses modificadores finais (entre outras coisas) ao salvar o arquivo.

Altamente recomendado.

Confira meu post do Eclipse Save Actions.

zvikico
fonte
1

Para argumentos, acho que não são necessários. Mostley eles apenas prejudicam a capacidade de leitura. Atribuir novamente uma variável de argumento é tão absurdamente estúpido que eu deveria estar bastante confiante de que elas podem ser tratadas como constantes de qualquer maneira.

O fato de o Eclipse colorir o vermelho final facilita a identificação de declarações de variáveis ​​no código, o que acho que melhora a capacidade de leitura na maioria das vezes.

Eu tento impor a regra de que toda e qualquer variável deve ser final, pois não há um motivo válido para isso. É muito mais fácil responder "o que é essa variável?" questione se você apenas precisa encontrar a iniciação e ter certeza de que é isso.

Na verdade, fico bastante nervoso com as variáveis ​​não finais hoje em dia. É como a diferença entre ter uma faca pendurada em um fio sobre sua cabeça ou apenas tê-la na sua gaveta da cozinha ...

Uma variável final é apenas uma boa maneira de atribuir valores.

Uma variável não final está vinculada a parte de algum algoritmo propenso a erros.

Um recurso interessante é que, quando a opção de usar uma variável fora de questão para um algoritmo na maioria das vezes, a solução é escrever um método, o que geralmente melhora significativamente o código.

John Nilsson
fonte
1

Eu tenho codificado por um tempo agora e uso final sempre que posso. Depois de fazer isso por um tempo (para variáveis, parâmetros de método e atributos de classe), posso dizer que 90% (ou mais) de minhas variáveis ​​são realmente finais. Acho que o benefício de NÃO ter variáveis ​​modificadas quando você não deseja (vi isso antes e às vezes é uma dor) paga pela digitação extra e pelas palavras-chave "finais" extras no seu código.

Dito isto, se eu projetasse uma linguagem, tornaria todas as variáveis ​​finais, a menos que modificadas por alguma outra palavra-chave.

Não uso muito final para classes e métodos, pensou. Essa é uma opção de design mais ou menos complicada, a menos que sua classe seja uma classe de utilitário (nesse caso, você deve ter apenas um construtor privado).

Eu também uso Collections.unmodifiable ... para criar listas não modificáveis ​​quando preciso.

Ravi Wallau
fonte
0

O uso de classes locais anônimas para ouvintes de eventos e esse é um padrão comum em Java. O uso mais comum da palavra-chave final é garantir que as variáveis ​​no escopo sejam acessíveis ao ouvinte par.

No entanto, se você precisar colocar muitas instruções finais em seu código. Essa pode ser uma boa dica de que você está fazendo algo errado.

O artigo postado acima fornece este exemplo:

public void doSomething(int i, int j) {
    final int n = i + j; // must be declared final

    Comparator comp = new Comparator() {
        public int compare(Object left, Object right) {
            return n; // return copy of a local variable
        }
    };
}
Hans Sjunnesson
fonte
0

Eu o uso para constantes métodos internos e externos.

Às vezes, eu o uso apenas para métodos, porque não sei se uma subclasse NÃO deseja substituir um determinado método (por qualquer motivo).

Quanto às classes, apenas para algumas classes de infraestrutura, usei a classe final.

O IntelliJ IDEA avisa se um parâmetro de função é gravado dentro de uma função. Então, parei de usar final para argumentos de função. Também não os vejo na biblioteca Java Runtime.

anjanb
fonte
0

Eu mal uso final em métodos ou classes porque gosto de permitir que as pessoas os substituam.

Caso contrário, eu só uso finalmente se for um public/private static final type SOME_CONSTANT;

jjnguy
fonte
Hmm..edited em um place..still é diz finalmente na segunda linha ;-)
Onipotente
Permitir que as pessoas sobrescrevam valores é a única fonte maior de surpresas e erros difíceis de descobrir
RAY
0

Marcar a final da classe também pode fazer com que algumas ligações de método ocorram no tempo de compilação, em vez de em tempo de execução. Considere "v2.foo ()" abaixo - o compilador sabe que B não pode ter uma subclasse, portanto foo () não pode ser substituído, portanto a implementação a ser chamada é conhecida no momento da compilação. Se a classe B NÃO estiver marcada como final, é possível que o tipo real de v2 seja alguma classe que estenda B e substitua foo ().

class A {
    void foo() {
        //do something
    }
}
final class B extends A {
    void foo() {
    }
}
class Test {
    public void t(A v1, B v2) {
        v1.foo();
        v2.foo();
    }
}
Eugene
fonte
-1

O uso final de constantes é fortemente recomendado. No entanto, eu não o usaria para métodos ou classes (ou pelo menos pensaria nisso por um tempo), porque torna os testes mais difíceis, se não impossíveis. Se você absolutamente precisar finalizar uma classe ou método, verifique se essa classe implementa alguma interface, para que você possa ter uma simulação implementando a mesma interface.

Paweł Hajdan
fonte
Isso não torna os testes mais difíceis porque você deve usar as interfaces de qualquer maneira.
26430 Chris Vest