Porquê declarar variáveis finais dentro de métodos? [duplicado]

70

Estudando algumas classes de Android, percebi que a maioria das variáveis de métodos são declarados como finais.

Exemplo de código retirado da classe android.widget.ListView:

/**
* @return Whether the list needs to show the top fading edge
*/
private boolean showingTopFadingEdge() {
    final int listTop = mScrollY + mListPadding.top;
    return (mFirstPosition > 0) || (getChildAt(0).getTop() > listTop);
}

/**
 * @return Whether the list needs to show the bottom fading edge
 */
private boolean showingBottomFadingEdge() {
    final int childCount = getChildCount();
    final int bottomOfBottomChild = getChildAt(childCount - 1).getBottom();
    final int lastVisiblePosition = mFirstPosition + childCount - 1;
    final int listBottom = mScrollY + getHeight() - mListPadding.bottom;
    return (lastVisiblePosition < mItemCount - 1)
                     || (bottomOfBottomChild < listBottom);
}

Qual é a intenção de usar a palavra-chave final nesses casos?

    
por Rodrigo 22.10.2011 / 06:43
fonte

7 respostas

59

Eu diria que isso se deve a força do hábito . O programador que escreveu esse código sabia, enquanto escrevia, que os valores das variáveis finais nunca deveriam ser alterados após a atribuição, e assim os tornavam finais. Qualquer tentativa de atribuir um novo valor a uma variável final após a atribuição resultará em um erro do compilador.

Como os hábitos vão, não é ruim desenvolver. No mínimo, fazer uma variável final especifica a intenção do programador no momento da escrita. Isso é importante, pois pode dar aos programadores subsequentes que editam o código pausarem para pensar antes de começarem a alterar como essa variável é usada.

    
por 22.10.2011 / 10:13
fonte
51

Falando como um desenvolvedor Java que faz todas as variáveis final por padrão (e aprecia o fato de que o Eclipse pode fazer isso automaticamente), acho mais fácil raciocinar sobre o meu programa se as variáveis forem inicializadas uma vez e nunca forem alteradas novamente.

Por um lado, as variáveis não inicializadas não são mais motivo de preocupação, porque tentar usar uma variável final antes de ser inicializada resultará em um erro de compilação. Isso é particularmente útil para lógicas condicionais aninhadas, nas quais desejo garantir que abordei todos os casos:

final int result;
if (/* something */) {
  if (/* something else */) {
    result = 1;
  }
  else if (/* some other thing */) {
    result = 2;
  }
}
else {
  result = 3;
}
System.out.println(result);

Cobri todos os casos? (Dica: Não.) Com certeza, esse código nem vai compilar.

Mais uma coisa: em geral, sempre que você souber que algo é sempre verdade sobre uma variável, você deve tentar fazer com que sua linguagem seja aplicada. Fazemos isso todos os dias quando especificamos o tipo de uma variável, é claro: o idioma garantirá que os valores que não são desse tipo não possam ser armazenados nessa variável. Da mesma forma, se você sabe que uma variável não deve ser reatribuída porque ela já tem o valor que deve manter para todo o método, então você pode fazer com que o idioma imponha essa restrição declarando-a como final .

Por fim, há a questão do hábito. Outros mencionaram que isso é um hábito (+1 para Jon para que ), mas deixe-me dizer algo sobre por que você iria querer esse hábito. Se você está declarando campos em sua classe e não variáveis locais em um método, é possível que vários encadeamentos acessem esses campos ao mesmo tempo. Existem algumas exceções obscuras, mas, em geral, se um campo for final, cada thread que usa sua classe verá o mesmo valor para a variável. Por outro lado, se um campo não for final e vários segmentos estiverem usando sua classe, você precisará se preocupar com a sincronização explícita usando synchronized blocos e / ou classes de java.util.concurrent . A sincronização é possível, mas a programação já é bastante difícil. ;-) Então, se você apenas declara tudo final fora do hábito, muitos dos seus campos serão finais e você gastará o mínimo de tempo possível se preocupando com erros relacionados à sincronização e à simultaneidade .

Para saber mais sobre esse hábito, confira a dica "Minimizar a Mutabilidade" no Efetivo de Joshua Bloch Java .

Edit: @ Peter Taylor apontou que o exemplo acima também não compilará se a palavra-chave final for removida, o que é completamente correto. Quando avisei a favor de manter todas as variáveis locais finais, é porque eu queria tornar exemplos como o seguinte:

int result = 0;

// OK, time to cover all the cases!
if (/* something */) {
  if (/* something else */) {
    result = 1;
  }
  else if (/* some other thing */) {
    result = 2;
  }
  // Whoops, missed an "else" here. Too bad.
}
else {
  result = 3;
}
System.out.println(result);  // Works fine!

Usar uma nova variável em vez de reutilizar uma antiga é como posso dizer ao compilador que tenta cobrir todo o universo de possibilidades, e usar final variables me obriga a usar uma nova variável em vez de reciclar uma antiga.

Outra reclamação válida sobre este exemplo é que você deve evitar lógica condicional aninhada complexa em primeiro lugar. Isso é verdade, é claro, precisamente porque é difícil garantir que você cobriu todos os casos da maneira pretendida. No entanto, às vezes a lógica complexa não pode ser evitada. Quando minha lógica é complexa, quero que minhas variáveis sejam tão simples quanto possível, o que posso conseguir certificando-me de que os valores de minhas variáveis nunca mudem depois de serem inicializados.

    
por 22.10.2011 / 13:40
fonte
7

Não sabemos a resposta com certeza (a menos que perguntemos aos desenvolvedores originais), mas meus palpites são:

  1. Os programadores desses métodos podem ter sentido que adicionar "final" melhor expressa o propósito dessas variáveis.
  2. Os programadores podem estar usando uma ferramenta que adiciona automaticamente "final" onde puder.
  3. Poderia ser um truque de otimização: eu não tenho o conhecimento / ferramentas para verificar isso sozinho, mas estaria interessado em saber de qualquer forma.
por 22.10.2011 / 07:56
fonte
6

Outro motivo é poder usá-los em classes anônimas internas:

public interface MyInterface {
    void foo();
} 

void myMethod() {
    final String asdf = "tada";//if not final cannot be used in inner classes
    new MyInterface() {
        @Override
        public void foo() {
             String myNewString = asdf;//does not compile only if asdf is final
        }
    }
}
    
por 09.08.2013 / 17:31
fonte
4

Para cometer erros bobos menos frequentes. final certifica-se de que a variável sempre aponta para seu primeiro objeto atribuído, e qualquer tentativa de mudança contará como um erro de tempo de compilação.

Você também pode perguntar "por que declarar explicitamente o tipo de variável?" ou "por que declarar métodos como constantes em C ++?". Mesma razão.

    
por 22.10.2011 / 14:39
fonte
2

Aqui o motivo: Evitando a redesignação para variáveis / parâmetros locais. Isso aumentaria a legibilidade e evitaria alguns erros estúpidos.

link

Trecho:

Java 1.1 and later versions allow you to mark a parameter as final; this prevents assignment to the variable. It still allows you to modify the object the variable refers to. I always treat my parameters as final, but I confess I rarely mark them so in the parameter list.

    
por 09.08.2013 / 17:22
fonte
0
Honestamente, eu não acho que haveria uma razão real para fazer uma variável final neste contexto. Eu suponho que eles copiem e colem o código, ou eles realmente querem desencorajar qualquer um que adicione mais código a esse método de fazer reatribuições para essas variáveis.

    
por 22.10.2011 / 07:18
fonte

Tags