Tornar os argumentos do método java finais

91

Que diferença isso finalfaz entre o código abaixo. Há alguma vantagem em declarar os argumentos como final.

public String changeTimezone( Timestamp stamp, Timezone fTz, Timezone toTz){  
    return ....
}

public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
        final Timezone toTz){
    return ....
}
John
fonte
4
Existem analisadores de código que avisam se um parâmetro é reutilizado ou reatribuído. (O mesmo para variáveis ​​locais) IMHO, esta é a melhor maneira de capturar tais parâmetros se você achar que alterá-los é indesejável.
Peter Lawrey
Acho que o Java deve tornar todos os argumentos do método de entrada finais por padrão. E se eu quiser modificar a referência, terei que fazer isso manualmente. Dessa forma, o fator de culpa impediria muitos desses casos.
Sid,

Respostas:

131

Como um parâmetro de método formal é uma variável local, você pode acessá-los de classes anônimas internas apenas se eles forem declarados como finais.

Isso evita que você declare outra variável final local no corpo do método:

 void m(final int param) {
        new Thread(new Runnable() {
            public void run() {
                System.err.println(param);
            }
        }).start();
    }
PeterMmm
fonte
27
+1: Este é um caso de uso importante e o único momento em que você precisa dele. (O resto do tempo é apenas uma questão de o que é conveniente para ajudar o programador.)
Donal Fellows
3
Posso perguntar o motivo por trás disso?
KodeWarrior
20
Não é mais necessário com Java 8.
Amit Parashar
3
@simgineer leia aqui: stackoverflow.com/questions/28408109/…
PeterMmm
3
@AmitParashar True, mas Java 8 está apenas evitando que você tenha que usar a palavra-chave "final" toda vez que você tiver que usar a variável em uma classe interna ... A realidade é que o compilador está apenas tornando a finalidade implícita, você ainda precisa que a variável seja efetivamente final ... Então, você ainda obterá um erro de tempo de compilação caso tente atribuí-la mais tarde! Java 8 : SNEAK 100 :)
varun
38

Extraia da palavra final na palavra-chave final

Parâmetros Finais

O exemplo a seguir declara os parâmetros finais:

public void doSomething(final int i, final int j)
{
  // cannot change the value of i or j here...
  // any change would be visible only inside the method...
}

final é usado aqui para garantir que os dois índices i e j não sejam redefinidos acidentalmente pelo método. É uma maneira prática de se proteger contra um bug insidioso que altera erroneamente o valor dos seus parâmetros. De modo geral, os métodos curtos são a melhor maneira de se proteger dessa classe de erros, mas os parâmetros finais podem ser uma adição útil ao seu estilo de codificação.

Observe que os parâmetros finais não são considerados parte da assinatura do método e são ignorados pelo compilador ao resolver chamadas de método. Os parâmetros podem ser declarados finais (ou não) sem influência em como o método é sobrescrito.

pgras
fonte
17
Pode ser melhor usar objetos em vez de primitivos para este exemplo, pois as mudanças primitivas sempre serão visíveis apenas dentro do método. E, no caso de objetos, você ainda pode alterá-los. Você simplesmente não pode apontar para um novo objeto. Na verdade, agora que penso nisso, o final não muda nada em comparação a deixá-lo de fora, a não ser salvar uma declaração de variável com AICs e fazer com que o compilador aponte modificações acidentais de parâmetros que você não deseja modificar por algum motivo .
Rob Grant
26

A final evita que você atribua um novo valor à variável e isso pode ser útil para detectar erros de digitação. Estilisticamente, você pode querer manter os parâmetros recebidos inalterados e atribuir apenas às variáveis ​​locais, portanto, o final ajudaria a reforçar esse estilo.

Devo admitir que raramente me lembro de usar final para parâmetros, talvez devesse.

public int example(final int basicRate){
    int discountRate;

    discountRate = basicRate - 10;
    // ... lots of code here 
    if ( isGoldCustomer ) {
        basicRate--;  // typo, we intended to say discountRate--, final catches this
    }
    // ... more code here

    return discountRate;
}
djna
fonte
1
Ótimo exemplo de como declarar argumentos como finais pode ser útil. Eu sou parcial para isso, mas eles também são um bocado para 3+ parâmetros.
JoseHdez_2
14

Não faz muita diferença. Significa apenas que você não pode escrever:

stamp = null;
fTz = new ...;

mas você ainda pode escrever:

stamp.setXXX(...);
fTz.setXXX(...);

É principalmente uma dica para o programador de manutenção que o segue de que você não vai atribuir um novo valor ao parâmetro em algum lugar no meio do seu método onde não seja óbvio e possa, portanto, causar confusão.

Adrian Pronk
fonte
3

A palavra-chave final, quando usada para parâmetros / variáveis ​​em Java, marca a referência como final. No caso de passar um objeto para outro método, o sistema cria uma cópia da variável de referência e a passa para o método. Ao marcar as novas referências como finais, você as protege de reatribuição. Às vezes, é considerada uma boa prática de codificação.

Sid
fonte
1
Tenho que acrescentar algo: se os parâmetros são primitivos, não vejo nenhuma diferença. Além disso, se os parâmetros são Coleções (uma lista de objetos ...), adicionar final não pode evitar que sejam modificados.
Sam003
1
A imutabilidade é sempre uma característica desejável. Java não vem pronto para uso. Tornar as variáveis ​​finais pelo menos garante a integridade da referência.
Sid
1
Concordo. Mas se realmente queremos alcançar a imutabilidade dos objetos, podemos tentar fazer um clone profundo.
Sam003
2

Para o corpo deste método, a finalpalavra - chave evitará que as referências de argumento sejam acidentalmente reatribuídas, dando um erro de compilação nesses casos (a maioria dos IDEs reclamará imediatamente). Alguns podem argumentar que o uso finalgeral sempre que possível irá acelerar as coisas, mas esse não é o caso em JVMs recentes.

dimitrisli
fonte
2

Duas vantagens que vejo estão listadas:

1 Marcar o argumento do método como final evita a reatribuição do argumento dentro do método

De seu exemplo

    public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
            final Timezone toTz){
    
    // THIS WILL CAUSE COMPILATION ERROR as fTz is marked as final argument

      fTz = Calendar.getInstance().getTimeZone();     
      return ..
    
    }

Em um método complicado, marcar os argumentos como finais ajudará na interpretação acidental desses argumentos como variáveis ​​locais de métodos e reatribuir como compilador sinalizará esses casos conforme mostrado no exemplo.

2 Passando o argumento para uma classe interna anônima

Como um parâmetro de método formal é uma variável local, você pode acessá-los de classes anônimas internas apenas se eles forem declarados como finais.

Santhosh Urumese
fonte
1

- No passado (antes do Java 8 :-))

O uso explícito da palavra-chave "final" afetou a acessibilidade da variável de método para classes anônimas internas.

- Na linguagem moderna (Java 8+), não há necessidade desse uso:

Java introduziu variáveis ​​"efetivamente finais". As variáveis ​​locais e os parâmetros do método são considerados finais se o código não implicar na alteração do valor da variável. Portanto, se você vir essa palavra-chave em Java8 +, pode presumir que é desnecessária. A introdução de "efetivamente final" nos faz digitar menos código ao usar lambdas.

Witold Kaczurba
fonte
0

É apenas uma construção em Java para ajudá-lo a definir um contrato e cumpri-lo. Uma discussão semelhante aqui: http://c2.com/cgi/wiki?JavaFinalConsideredEvil

BTW - (como diz o twiki), marcar args como final é geralmente redundante se você estiver seguindo bons princípios de programação e tiver feito redesignar / redefinir a referência de argumento de entrada.

No pior caso, se você redefinir a referência args, isso não afetará o valor real passado para a função - uma vez que apenas uma referência foi passada.

madhurtanwani
fonte
0

Estou falando de marcar variáveis ​​e campos em geral - não se aplica apenas a argumentos de método. (Marcar métodos / classes finais é uma coisa totalmente diferente).

É um favor aos leitores / futuros mantenedores do seu código. Juntamente com um nome adequado da variável, é útil e tranquilizador para o leitor do seu código ver / compreender o que as variáveis ​​em questão representam - e é reconfortante para o leitor que sempre que você vir a variável no mesmo escopo, o significado permanece o mesmo, então (s) ele não tem que coçar a cabeça para sempre descobrir o que uma variável significa em cada contexto. Vimos muitos abusos de "reutilização" de variáveis, o que torna até mesmo um pequeno trecho de código difícil de entender.

RAIO
fonte
-3

A palavra-chave final impede que você atribua um novo valor ao parâmetro. Eu gostaria de explicar isso com um exemplo simples

Suponha que temos um método

Método 1(){

Data dateOfBirth = nova data ("01/01/2009");

método2 (dateOfBirth);

método3 (dateOfBirth); }

public mehod2 (data dateOfBirth) {
....
....
....
}

public mehod2 (data dateOfBirth) {
....
....
....
}

No caso acima, se "dateOfBirth" receber um novo valor no método2, isso resultaria em uma saída errada do método3. Como o valor que está sendo passado para o método3 não é o que era antes de ser passado para o método2. Portanto, para evitar esta palavra-chave final é usada para parâmetros.

E esta também é uma das melhores práticas de codificação Java.

Kamal
fonte
5
Isso não está certo. Mesmo se o argumento dateOfBirth for alterado para outro valor em method2 (), isso não terá efeito em method2 (), já que Java passa por valor e não por referência.
Flo