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.