¿Por qué declarar las variables finales dentro de los métodos? [duplicar]

70

Al estudiar algunas clases de Android, me di cuenta de que la mayoría de las variables de los métodos se declaran como finales.

Código de ejemplo tomado de la clase 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);
}

¿Cuál es la intención de usar la palabra clave final en estos casos?

    
pregunta Rodrigo 22.10.2011 - 06:43

7 respuestas

59

Yo diría que esto se debe a fuerza de costumbre . El programador que escribió este código supo, mientras lo escribía, que los valores para las variables finales nunca deberían cambiarse después de la asignación, y así los hizo definitivos. Cualquier intento de asignar un nuevo valor a una variable final después de la asignación resultará en un error del compilador.

Según los hábitos, no es un mal desarrollo. Al menos, al hacer una variable final se especifica el intento del programador en el momento de escribir. Esto es importante ya que podría hacer que los programadores subsiguientes que editan el código se detengan para pensar antes de comenzar a cambiar cómo se usa esa variable.

    
respondido por el Jon 22.10.2011 - 10:13
51

Hablando como un desarrollador de Java que hace que todas las variables final de forma predeterminada (y que aprecie el hecho de que Eclipse pueda hacer esto automáticamente), me resulta más fácil razonar acerca de mi programa si las variables se inicializan una vez y nunca se vuelven a cambiar.

Por un lado, las variables sin inicializar ya no son una preocupación, porque tratar de usar una variable final antes de que se haya inicializado resultará en un error de compilación. Esto es particularmente útil para la lógica condicional anidada, donde quiero asegurarme de cubrir todos los 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);

¿Cubrí todos los casos? (Consejo: No.) Efectivamente, este código ni siquiera se compilará.

Una cosa más: en general, cada vez que sepa que algo siempre es cierto acerca de una variable, debe intentar que su idioma lo aplique. Lo hacemos todos los días cuando especificamos el tipo de una variable, por supuesto: el idioma asegurará que los valores que no son de ese tipo no se puedan almacenar en esa variable. Del mismo modo, si sabe que no se debe reasignar una variable porque ya tiene el valor que debería mantener para todo el método, puede obtener el lenguaje para aplicar esa restricción declarándola final .

Por último, está la cuestión del hábito. Otros han mencionado que este es un hábito (+1 a Jon para eso ), pero déjame decirte algo sobre por qué querrías este hábito. Si está declarando campos en su clase y no variables locales en un método, es posible que múltiples hilos accedan a esos campos al mismo tiempo. Hay algunas excepciones oscuras, pero en general, si un campo es final, entonces cada hilo que usa su clase verá el mismo valor para la variable. A la inversa, si un campo no es final y hay varios subprocesos que usan su clase, deberá preocuparse por la sincronización explícita utilizando synchronized bloques y / o clases de java.util.concurrent . La sincronización es posible, pero la programación ya es bastante difícil. ;-) Por lo tanto, si solo declara todo final por costumbre, muchos de sus campos serán definitivos y pasará el menor tiempo posible preocupándose por la sincronización y los errores relacionados con la concurrencia. .

Para más información sobre este hábito, consulte la sugerencia de "Minimizar la mutabilidad" en efectivo de Joshua Bloch Java .

Editar: @Peter Taylor ha señalado que el ejemplo anterior tampoco se compilará si se elimina la palabra clave final , lo cual es completamente correcto. Cuando aconsejé a favor de mantener todas las variables locales definitivas, es porque quería hacer imposible ejemplos como los siguientes:

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 una nueva variable en lugar de reutilizar una antigua es cómo puedo decirle al compilador que tratar de cubrir todo el universo de posibilidades, y usar las variables final me obliga a usar una nueva variable en lugar de reciclar una antigua.

Otra queja válida sobre este ejemplo es que debes evitar la lógica condicional anidada compleja en primer lugar. Eso es cierto, por supuesto, precisamente porque es difícil asegurarse de cubrir todos los casos de la forma que pretendía. Sin embargo, a veces la lógica compleja no se puede evitar. Cuando mi lógica es compleja, quiero que mis variables sean lo más simples de razonar posible, lo que puedo lograr asegurándome de que los valores de mis variables nunca cambien después de que se inicialicen.

    
respondido por el Joe Carnahan 22.10.2011 - 13:40
7

No podemos saber la respuesta con seguridad (a menos que preguntemos a los desarrolladores originales), pero mis conjeturas son:

  1. Los programadores de estos métodos pueden haber sentido que agregar "final" expresa mejor el propósito de esas variables.
  2. Los programadores pueden estar usando una herramienta que agrega automáticamente "final" donde puede.
  3. Podría ser un truco de optimización: no tengo los conocimientos / herramientas para verificar esto por mí mismo, pero me interesaría saberlo de cualquier manera.
respondido por el Kramii 22.10.2011 - 07:56
6

Otra razón es poder usarlas en clases 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
        }
    }
}
    
respondido por el m3th0dman 09.08.2013 - 17:31
4

Para cometer errores tontos menos frecuentes. final se asegura de que la variable siempre apunte a su primer objeto asignado, y cualquier intento de cambio contará como un error en tiempo de compilación.

También puede preguntar "¿por qué declarar explícitamente el tipo de una variable?", o "¿por qué declarar los métodos como constantes en C ++?". La misma razón.

    
respondido por el Yam Marcovic 22.10.2011 - 14:39
2

Aquí la razón por la que: Evitar la reasignación a variables / parámetros locales. Esto aumentaría la legibilidad y evitaría algunos errores estúpidos.

enlace

Extracto:

  

Java 1.1 y las versiones posteriores le permiten marcar un parámetro como final;   Esto evita la asignación a la variable. Todavía te permite   Modificar el objeto al que se refiere la variable. Siempre trato mis parametros   como final, pero confieso que rara vez los marca en la lista de parámetros.

    
respondido por el Mik378 09.08.2013 - 17:22
0

Honestamente, no creo que haya una razón real para hacer una variable final en este contexto. Supongo que o bien copian y pegan el código, o simplemente quieren disuadir a cualquiera que agregue más código a ese método para que no haga reasignaciones a esas variables.

    
respondido por el programmx10 22.10.2011 - 07:18

Lea otras preguntas en las etiquetas