Quando se deve usar final para parâmetros de método e variables ​​locais?

Eu encontrei algumas referências ( por exemplo ) que sugerem o uso final tanto quanto possível e eu estou querendo saber como isso é importante. Isto é principalmente no contexto dos parâmetros do método e variables ​​locais, não methods finais ou classs. Para constantes, faz sentido óbvio.

Por um lado, o compilador pode fazer algumas otimizações e torna a intenção do programador mais clara. Por outro lado, adiciona verbosidade e as otimizações podem ser triviais.

É algo que eu deveria fazer um esforço para lembrar de fazer?

Obsessar-se por:

  • Campos finais – Marcar os campos como final força-os a serem definidos no final da construção, tornando essa referência de campo imutável. Isso permite a publicação segura de campos e pode evitar a necessidade de synchronization em leituras posteriores. (Observe que, para uma referência de object, somente a referência de campo é imutável – as coisas a que a referência de object se refere ainda podem mudar e isso afeta a imutabilidade.)
  • Campos estáticos finais – Embora eu use enums agora em muitos dos casos em que usei campos finais estáticos.

Considere, mas use de maneira judiciosa:

  • Classes finais – O design do Framework / API é o único caso em que o considero.
  • Métodos finais – Basicamente iguais às classs finais. Se você estiver usando padrões de método de template como loucos e marcando coisas finais, você provavelmente está confiando demais em inheritance e não o suficiente em delegação.

Ignore a menos que se sinta anal:

  • parameters de methods e variables ​​locais – Eu RARAMENTE faço isso em grande parte porque sou preguiçoso e acho que ele desordena o código. Admito que a marcação de parâmetros e variables ​​locais que não vou modificar é “mais clara”. Eu queria que fosse o padrão. Mas não é e eu acho o código mais difícil de entender com as finais em todo. Se eu estiver no código de outra pessoa, eu não vou retirá-los, mas se eu estiver escrevendo um novo código, não os colocarei. Uma exceção é o caso em que você tem que marcar algo final para poder acessar de dentro de uma class interna anônima.

É algo que eu deveria fazer um esforço para lembrar de fazer?

Não, se você estiver usando o Eclipse, porque você pode configurar uma ação Salvar para adicionar automaticamente esses modificadores finais para você. Então você obtém os benefícios por menos esforço.

Os benefícios de tempo de desenvolvimento de “final” são pelo menos tão significativos quanto os benefícios de tempo de execução. Diz aos futuros editores do código algo sobre suas intenções.

Marcar uma aula “final” indica que você não fez um esforço durante o design ou a implementação da class para lidar com a extensão normalmente. Se os leitores puderem fazer alterações na class e quiserem remover o modificador “final”, poderão fazê-lo por sua conta e risco. Cabe a eles garantir que a turma cuide bem da extensão.

Marcar uma variável “final” (e atribuí-la ao construtor) é útil com a injeção de dependência. Indica a natureza “colaboradora” da variável.

Marcar um método “final” é útil em classs abstratas. Delineia claramente onde estão os pontos de extensão.

Eu encontrei parâmetros do método de marcação e locais como final é útil como uma ajuda de refatoração quando o método em questão é uma confusão incompreensível várias páginas. Sprinkle final liberalmente, ver o que “não pode atribuir a variável final” erros o compilador (ou o seu IDE) joga, e você só pode descobrir porque a variável chamada “dados” acaba nulo mesmo que vários (desatualizados) comentários juram que não pode acontecer.

Então você pode corrigir alguns dos erros, substituindo as variables ​​reutilizadas com novas variables ​​declaradas mais próximas do ponto de uso. Então você descobre que pode envolver partes inteiras do método em chaves de escopo, e de repente você está com um pressionamento de tecla IDE longe de “Extrair Método” e seu monstro ficou mais compreensível.

Se o seu método já não é um desastre insustentável, acho que pode ser útil tornar as coisas definitivas para desencorajar as pessoas a transformá-las em destroços; mas se for um método curto (veja: não é sustentável), então você corre o risco de adicionar muita verbosidade. Em particular, as assinaturas de function Java são difíceis o suficiente para caber em 80 caracteres sem adicionar mais seis por argumento!

Bem, isso tudo depende do seu estilo … se você gostar de ver a final quando não estiver modificando a variável, então use-a. Se você não gosta de vê-lo … então deixe de fora.

Eu, pessoalmente, gosto da menor quantidade de detalhes possível, então tento evitar usar palavras-chave extras que não são realmente necessárias.

Eu prefiro linguagens dinâmicas, então provavelmente não é surpresa eu gostar de evitar a verbosidade.

Então, eu diria apenas escolher a direção que você está inclinando e apenas ir com ele (seja qual for o caso, tente ser consistente).


Como uma nota lateral, eu trabalhei em projetos que usam e não usam tal padrão, e eu não vi nenhuma diferença na quantidade de erros ou erros … Eu não acho que seja um padrão que imensamente Melhore sua contagem de erros ou qualquer coisa, mas novamente é estilo, e se você gosta de expressar a intenção de não modificá-lo, então vá em frente e use-o.

Eu uso final o tempo todo para tornar o Java mais baseado em expressões. Veja que as condições do Java ( if,else,switch ) não são baseadas em expressões que eu sempre detestei especialmente se você está acostumado com functional programming (isto é, ML, Scala ou Lisp).

Assim, você deve tentar sempre (IMHO) usar variables ​​finais ao usar condições.

Deixe-me lhe dar um exemplo:

  final String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; case JOB_POSTING_IMPORT: name = "Blah"; break; default: throw new IllegalStateException(); } 

Agora, se adicionar outra declaração de case e não definir o name o compilador irá falhar. O compilador também falhará se você não quebrar em todos os casos (que você definiu a variável). Isso permite tornar o Java muito semelhante às expressões let de Lisp e faz com que seu código não seja massivamente recuado (devido a variables ​​de escopo léxico).

E como @Recurse observou (mas aparentemente -1 eu) você pode fazer o anterior sem fazer String name final para obter o erro do compilador (que eu nunca disse que você não podia) mas você poderia facilmente fazer o erro do compilador ir embora nome da configuração após a instrução switch que joga fora a semântica da expressão ou pior esquecendo de break que você não pode causar um erro (apesar do que @Recurse diz) sem usar final :

  String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; //break; whoops forgot break.. //this will cause a compile error for final ;P @Recurse case JOB_POSTING_IMPORT: name = "Blah"; break; } // code, code, code // Below is not possible with final name = "Whoops bug"; 

Por causa do nome da configuração do bug (além de esquecer de break que também é outro bug) agora eu posso acidentalmente fazer isso:

  String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; //should have handled all the cases for pluginType } // code, code, code // Below is not possible with final name = "Whoops bug"; 

A variável final força uma única avaliação de qual nome deve ser. Semelhante a como uma function que possui um valor de retorno sempre deve retornar um valor (ignorar exceções) o bloco de troca de nome terá que resolver o nome e assim vinculado a esse bloco de comutador que facilita a refatoração de partes do código (ou seja, refator do Eclipse: método extract) .

O acima em OCaml:

 type plugin = CandidateExport | JobPostingImport let p = CandidateExport let name = match p with | CandidateExport -> "Candidate Stuff" | JobPostingImport -> "Blah" ;; 

A match ... with ... avaliada como uma function, ou seja, uma expressão. Observe como se parece com a nossa declaração de switch.

Aqui está um exemplo em Scheme (Racket ou Chicken):

 (define name (match b ['CandidateExport "Candidate Stuff"] ['JobPostingImport "Blah"])) 

Se você estiver escrevendo um aplicativo que alguém terá que ler o código depois de, digamos, 1 ano, então sim, use a variável final que não deve ser modificada o tempo todo. Ao fazer isso, seu código será mais “autodocumentado” e você também reduzirá a chance de outros desenvolvedores fazerem coisas tolas como usar uma constante local como uma variável temporária local.

Se você está escrevendo algum código descartável, então, não se preocupe em identificar todas as constantes e torná-las definitivas.

É útil em parâmetros para evitar alterar o valor do parâmetro por acidente e introduzir um bug sutil. Eu uso para ignorar essa recomendação, mas depois de passar umas 4 horas. em um método horrível (com centenas de linhas de código e múltiplos fors, ifs nesteds e todo tipo de práticas ruins) eu recomendaria que você fizesse isso.

  public int processSomethingCritical( final int x, final int y ){ // hundreds of lines here // for loop here... int x2 = 0; x++; // bug aarrgg... // hundreds of lines there // if( x == 0 ) { ... } 

Claro que em um mundo perfeito isso não aconteceria, mas … bem … às vezes você tem que suportar outros códigos. 🙁

Vou usar o final o máximo que puder. Isso será sinalizado se você alterar o campo sem querer. Eu também configuro os parâmetros do método para final. Ao fazê-lo, peguei vários erros do código que assumi quando tentaram “definir” um parâmetro esquecendo que Java passava por valor.

Não está claro a partir da pergunta se isso é óbvio, mas fazer um parâmetro de método final afeta apenas o corpo do método. NÃO transmite qualquer informação interessante sobre as intenções do método para o invocador. O object transmitido ainda pode sofrer mutação no método (os finais não são consts) e o escopo da variável está dentro do método.

Para responder a sua pergunta precisa, eu não me incomodaria em fazer uma instância ou variável local (incluindo parâmetros de método) final, a menos que o código exigisse (por exemplo, a variável é referenciada de uma class interna), ou para esclarecer alguma lógica realmente complicada.

Por exemplo, variables, eu as tornaria finais se elas fossem logicamente constantes.

Existem muitos usos para a variável final . Aqui estão apenas alguns

Constantes finais

  public static class CircleToolsBetter { public final static double PI = 3.141; public double getCircleArea(final double radius) { return (Math.pow(radius, 2) * PI); } } 

Isso pode ser usado para outras partes de seus códigos, ou acessado por outras classs, assim, se você alterasse o valor, não teria que alterá-las uma a uma.

Variáveis ​​Finais

 public static String someMethod(final String environmentKey) { final String key = "env." + environmentKey; System.out.println("Key is: " + key); return (System.getProperty(key)); } } 

Nesta class, você constrói uma variável final com escopo definido que adiciona um prefixo ao parâmetro environmentKey. Nesse caso, a variável final é final apenas dentro do escopo de execução, que é diferente em cada execução do método. Cada vez que o método é inserido, o final é reconstruído. Assim que é construído, não pode ser alterado durante o escopo da execução do método. Isso permite que você corrija uma variável em um método pela duração do método. ver abaixo:

 public class FinalVariables { public final static void main(final String[] args) { System.out.println("Note how the key variable is changed."); someMethod("JAVA_HOME"); someMethod("ANT_HOME"); } } 

Constantes finais

 public double equation2Better(final double inputValue) { final double K = 1.414; final double X = 45.0; double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M); double powInputValue = 0; if (result > 360) { powInputValue = X * Math.sin(result); } else { inputValue = K * Math.sin(result); // < = Compiler error } 

Estes são especialmente úteis quando você tem realmente longas linhas de códigos, e isso irá gerar erro do compilador para que você não corra para o erro lógico / comercial quando alguém acidentalmente muda variables ​​que não devem ser alteradas.

Coleções finais

Caso diferente quando estamos falando de collections, você precisa defini-las como um unmodifiable.

  public final static Set VALID_COLORS; static { Set temp = new HashSet( ); temp.add(Color.red); temp.add(Color.orange); temp.add(Color.yellow); temp.add(Color.green); temp.add(Color.blue); temp.add(Color.decode("#4B0082")); // indigo temp.add(Color.decode("#8A2BE2")); // violet VALID_COLORS = Collections.unmodifiableSet(temp); } 

caso contrário, se você não definir como inalterável:

 Set colors = Rainbow.VALID_COLORS; colors.add(Color.black); // < = logic error but allowed by compiler 

As Classes Finais e os Métodos Finais não podem ser estendidos ou sobrescritos, respectivamente.

EDIT: PARA ABORDAR O PROBLEMA DA CLASSE FINAL EM RELAÇÃO À ENCAPSULAÇÃO:

Existem duas maneiras de fazer uma aula final. A primeira é usar a palavra-chave final na declaração da class:

 public final class SomeClass { // . . . Class contents } 

A segunda maneira de fazer uma final de class é declarar todos os seus construtores como privados:

 public class SomeClass { public final static SOME_INSTANCE = new SomeClass(5); private SomeClass(final int value) { } 

A marcação final evita o problema se você descobrir que é uma final, para demonstrar essa class de teste. parece público à primeira vista.

 public class Test{ private Test(Class beanClass, Class stopClass, int flags) throws Exception{ // . . . snip . . . } } 

Infelizmente, como o único construtor da class é privado, é impossível estender essa class. No caso da class Test, não há motivo para que a class seja final. A class Test é um bom exemplo de como as classs finais implícitas podem causar problemas.

Então você deve marcá-lo final quando você implicitamente faz um final de class fazendo seu construtor privado.

Um pouco de trade-off, como você menciona, mas eu prefiro o uso explícito de algo sobre o uso implícito. Isso ajudará a remover alguma ambiguidade para futuros mantenedores de código – mesmo que seja apenas você.

Se você tem classs internas (anônimas), e o método precisa acessar a variável do método de contenção, você precisa ter essa variável como final.

Fora isso, o que você disse está certo.

Use final palavra-chave final para uma variável se você estiver tornando essa variável immutable

Ao declarar a variável como final, ela ajuda os desenvolvedores a descartar possíveis problemas de modificação de variables ​​em ambientes altamente multiencadeados.

Com o lançamento do java 8, temos mais um conceito chamado ” effectively final variable “. Uma variável não final pode ser usada como variável final.

as variables ​​locais referenciadas a partir de uma expressão lambda devem ser finais ou efetivamente finais

Uma variável é considerada final efetiva se não for modificada após a boot no bloco local. Isso significa que agora você pode usar a variável local sem a palavra-chave final dentro de uma class anônima ou expressão lambda, desde que elas sejam efetivamente finais.

Até o Java 7, você não pode usar uma variável local não final dentro de uma class anônima, mas a partir do Java 8 você pode

Dê uma olhada neste artigo

Uma resposta muito simples, temos 3 casos com final com variables, final com methods e final com classs ..

1.Final com variável: você não pode atribuir esta variável mais de uma vez ..

2.Final com methods: você não pode sobrepor este método.

3.Final com classs: você não pode estender qualquer aula final

Primeiro de tudo, a palavra final é usada para fazer uma variável constante. Constante significa que não muda. Por exemplo:

 final int CM_PER_INCH = 2.54; 

Você declararia a variável final porque um centímetro por polegada não muda.

Se você tentar replace um valor final, a variável é o que foi declarado primeiro. Por exemplo:

 final String helloworld = "Hello World"; helloworld = "A String"; //helloworld still equals "Hello World" 

Existe um erro de compilation que é algo como:

 local variable is accessed from inner class, must be declared final 

Se sua variável não puder ser declarada final ou se você não quiser declará-la final tente isto:

 final String[] helloworld = new String[1]; helloworld[0] = "Hello World!"; System.out.println(helloworld[0]); helloworld[0] = "A String"; System.out.println(helloworld[0]); 

Isto irá imprimir:

 Hello World! A String