Não consigo entender onde a final
palavra-chave é realmente útil quando usada nos parâmetros do método.
Se excluirmos o uso de classes anônimas, legibilidade e declaração de intenção, isso parecerá quase inútil para mim.
A imposição de que alguns dados permaneçam constantes não é tão forte quanto parece.
Se o parâmetro for um primitivo, ele não terá efeito, pois o parâmetro é passado para o método como um valor e a alteração não terá efeito fora do escopo.
Se estamos passando um parâmetro por referência, a própria referência é uma variável local e se a referência for alterada de dentro do método, isso não teria nenhum efeito fora do escopo do método.
Considere o exemplo de teste simples abaixo. Este teste é aprovado, embora o método tenha alterado o valor da referência fornecida, ele não tem efeito.
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
fonte
int foo(int* bar)
e o últimoint foo(int* &bar)
. O último está passando um ponteiro por referência, o primeiro está passando uma referência por valor.Respostas:
Interromper a reatribuição de uma variável
Embora essas respostas sejam intelectualmente interessantes, não li a resposta curta e simples:
Seja a variável estática, variável membro, variável local ou variável argumento / parâmetro, o efeito é totalmente o mesmo.
Exemplo
Vamos ver o efeito em ação.
Considere este método simples, onde as duas variáveis ( arg e x ) podem ser atribuídas novamente a objetos diferentes.
Marque a variável local como final . Isso resulta em um erro do compilador.
Em vez disso, vamos marcar a variável de parâmetro como final . Isso também resulta em um erro do compilador.
Moral da história:
Nunca reatribuir argumentos
Como boa prática de programação (em qualquer idioma), você nunca deve atribuir novamente uma variável de parâmetro / argumento a um objeto que não seja o objeto passado pelo método de chamada. Nos exemplos acima, nunca se deve escrever a linha
arg =
. Como os humanos cometem erros e os programadores são humanos, vamos pedir ao compilador para nos ajudar. Marque cada variável de parâmetro / argumento como 'final' para que o compilador possa encontrar e sinalizar essas redesignações.Em retrospecto
Como observado em outras respostas ... Dado o objetivo de design original do Java de ajudar os programadores a evitar erros estúpidos, como ler além do final de uma matriz, o Java deveria ter sido projetado para impor automaticamente todas as variáveis de parâmetro / argumento como 'final'. Em outras palavras, argumentos não devem ser variáveis . Mas a retrospectiva é uma visão 20/20, e os designers de Java estavam com as mãos cheias na época.
Então, sempre adicione
final
a todos os argumentos?Devemos adicionar
final
a cada parâmetro de método declarado?➥ Adicione
final
apenas quando o código do método for longo ou complicado, onde o argumento pode ser confundido com uma variável local ou membro e possivelmente redesignado.Se você adotar a prática de nunca reatribuir um argumento, estará inclinado a adicionar um
final
a cada um. Mas isso é tedioso e torna a declaração um pouco mais difícil de ler.Para um código simples e curto, onde o argumento é obviamente um argumento, e não uma variável local nem uma variável membro, não me importo em adicionar o
final
. Se o código for bastante óbvio, sem nenhuma chance de eu ou qualquer outro programador fazer manutenção ou refatorar acidentalmente confundir a variável de argumento como algo que não seja um argumento, não se preocupe. No meu próprio trabalho, adicionofinal
apenas códigos mais longos ou mais envolvidos, nos quais um argumento pode ser confundido com uma variável local ou membro.Outro caso adicionado para a integridade
fonte
message = ( msg || 'Hello World"' )
. Simplesmente não há razão para não usar um var separado. O único custo é de alguns bytes de memória.message = ( msg || 'Hello World"' )
me arrisca depois acidentalmentemsg
. Quando o contrato que pretendo é "comportamento sem argumento nulo / indefinido é indistinguível de aprovação"Hello World"
", é uma boa prática de programação comprometê-lo no início da função. [Isso pode ser alcançado sem reatribuição, começando com,if (!msg) return myfunc("Hello World");
mas isso se torna complicado com vários argumentos.] Nos raros casos em que a lógica na função deve se importar se o padrão foi usado, prefiro designar um valor sentinela especial (de preferência público) .message
emsg
. Mas se ele chamar isso de algo comoprocessedMsg
ou algo mais que forneça contexto adicional - a chance de um erro é muito menor. Concentre-se no que ele diz e não em "como" ele diz. ;)Às vezes, é bom ser explícito (para facilitar a leitura) que a variável não muda. Aqui está um exemplo simples em que o uso
final
pode salvar algumas dores de cabeça possíveis:Se você esquecer a palavra-chave 'this' em um setter, a variável que você deseja definir não será definida. No entanto, se você usasse a
final
palavra-chave no parâmetro, o bug seria detectado no momento da compilação.fonte
Sim, excluir classes anônimas, legibilidade e declaração de intenção é quase inútil. Essas três coisas são inúteis?
Pessoalmente, costumo não usar
final
variáveis e parâmetros locais, a menos que esteja usando a variável em uma classe interna anônima, mas certamente consigo entender o ponto daqueles que desejam deixar claro que o valor do parâmetro em si não será alterado (mesmo se o objeto ao qual ele se refere muda seu conteúdo). Para aqueles que acham que isso aumenta a legibilidade, acho uma coisa totalmente razoável de se fazer.Seu ponto seria mais importante se alguém foram realmente alegando que se mantenha constante de dados de uma forma que não faz - mas eu não me lembro de ver tais reivindicações. Você está sugerindo que há um corpo significativo de desenvolvedores sugerindo que
final
tem mais efeito do que realmente tem?Edição: Eu realmente deveria ter resumido tudo isso com uma referência Monty Python; a pergunta parece um pouco semelhante a perguntar "O que os romanos já fizeram por nós?"
fonte
Deixe-me explicar um pouco sobre o único caso em que você tem de usar final, que Jon já mencionado:
Se você criar uma classe interna anônima no seu método e usar uma variável local (como um parâmetro do método) dentro dessa classe, o compilador o obrigará a tornar o parâmetro final:
Aqui, os parâmetros
from
eto
precisam ser finais para que possam ser usados dentro da classe anônima.O motivo desse requisito é o seguinte: Variáveis locais vivem na pilha; portanto, elas existem apenas enquanto o método é executado. No entanto, a instância de classe anônima é retornada do método, portanto, pode permanecer por muito mais tempo. Você não pode preservar a pilha, porque ela é necessária para chamadas de método subsequentes.
Portanto, o que o Java faz é colocar cópias dessas variáveis locais como variáveis de instância ocultas na classe anônima (você pode vê-las se examinar o código de bytes). Mas, se não foram finais, pode-se esperar a classe anônima e o método que vê mudanças na outra variável na variável. Para manter a ilusão de que há apenas uma variável em vez de duas cópias, ela deve ser final.
fonte
final
parâmetro de função e um parâmetro de função não final aos olhos do HotSpot?Eu uso final o tempo todo em parâmetros.
Isso adiciona muito? Na verdade não.
Eu desligaria? Não.
O motivo: eu encontrei 3 bugs em que as pessoas haviam escrito código incorreto e não conseguiram definir uma variável de membro nos acessadores. Todos os erros foram difíceis de encontrar.
Eu gostaria de ver isso tornar o padrão em uma versão futura do Java. A passagem por valor / referência gera um grande número de programadores juniores.
Mais uma coisa ... meus métodos tendem a ter um número baixo de parâmetros, portanto o texto extra em uma declaração de método não é um problema.
fonte
Usar final em um parâmetro de método não tem nada a ver com o que acontece com o argumento no lado do chamador. Ele serve apenas para marcá-lo como não sendo alterado dentro desse método. Enquanto tento adotar um estilo de programação mais funcional, meio que vejo o valor disso.
fonte
final
parâmetros nas interfaces / declarações de método abstrato.Pessoalmente, eu não uso final nos parâmetros do método, porque adiciona muita confusão às listas de parâmetros. Prefiro impor que os parâmetros do método não sejam alterados através de algo como o Checkstyle.
Para variáveis locais que uso final sempre que possível, deixo o Eclipse fazer isso automaticamente na minha configuração para projetos pessoais.
Eu certamente gostaria de algo mais forte como C / C ++ const.
fonte
Como o Java passa cópias de argumentos, sinto que a relevância
final
é bastante limitada. Eu acho que o hábito vem da era C ++, onde você pode proibir que o conteúdo de referência seja alterado fazendo umconst char const *
. Eu sinto que esse tipo de coisa faz você acreditar que o desenvolvedor é inerentemente estúpido pra caralho e precisa ser protegido contra todos os personagens que ele digita. Com toda a humildade, posso dizer, escrevo muito poucos bugs, embora o omitafinal
(a menos que não queira que alguém substitua meus métodos e classes). Talvez eu seja apenas um desenvolvedor da velha escola.fonte
Eu nunca uso final em uma lista de parâmetros, apenas adiciona confusão como os entrevistados anteriores disseram. Também no Eclipse, você pode definir a atribuição de parâmetros para gerar um erro, portanto, usar final em uma lista de parâmetros parece bastante redundante para mim. Curiosamente, quando ativei a configuração do Eclipse para a atribuição de parâmetros, gerando um erro, ele capturou esse código (é assim que me lembro do fluxo, não do código real.): -
Brincando de advogado do diabo, o que exatamente há de errado em fazer isso?
fonte
Resposta curta:
final
ajuda um pouquinho, mas ... em vez disso, use programação defensiva no lado do cliente.De fato, o problema
final
é que ele apenas impõe a referência inalterada, permitindo que os membros do objeto referenciado sejam mutados, sem o conhecimento do chamador. Portanto, a melhor prática a esse respeito é a programação defensiva do lado do chamador, criando instâncias profundamente imutáveis ou cópias profundas de objetos que correm o risco de serem invadidas por APIs inescrupulosas.fonte
Um motivo adicional para adicionar declarações finais aos parâmetros é que ajuda a identificar variáveis que precisam ser renomeadas como parte de uma refatoração "Extrair método". Eu descobri que adicionar final a cada parâmetro antes de iniciar uma refatoração de método grande rapidamente informa se há algum problema que eu precise resolver antes de continuar.
No entanto, geralmente os removo como supérfluos no final da refatoração.
fonte
Acompanhamento pelo que Michel post. Eu mesmo me fiz outro exemplo para explicá-lo. Espero que possa ajudar.
A partir do código acima, no método thisIsWhy () , na verdade , não atribuímos o [argumento MyObj obj] a uma referência real em MyParam. Em vez disso, usamos apenas o [argumento MyObj obj] no método dentro de MyParam.
Mas, após concluirmos o método thisIsWhy () , o argumento (objeto) MyObj ainda existe?
Parece que deveria, porque podemos ver no main que ainda chamamos o método showObjName () e ele precisa atingir obj . O MyParam ainda usa / alcança o argumento do método, mesmo o método já foi retornado!
Como o Java realmente consegue isso é gerar uma cópia também é uma referência oculta do argumento MyObj obj dentro do objeto MyParam (mas não é um campo formal em MyParam para que não possamos vê-lo)
Como chamamos "showObjName", ele usará essa referência para obter o valor correspondente.
Mas se não colocarmos o argumento final, o que leva a uma situação, podemos reatribuir uma nova memória (objeto) ao argumento MyObj obj .
Tecnicamente, não há conflito! Se nos for permitido fazer isso, abaixo será a situação:
Sem confrontos, mas "CONFUSO !!" Porque todos eles estão usando o mesmo "nome de referência", que é "obj" .
Para evitar isso, defina-o como "final" para evitar que o programador faça o código "propenso a erros".
fonte