java - software - sourcemaking
Usando el modificador "final" siempre que sea aplicable en Java (25)
Casi no utilizo métodos o clases finales porque me gusta permitir que las personas los anulen.
De lo contrario, solo uso finalmente si es un public/private static final type SOME_CONSTANT;
En Java, existe la práctica de declarar todas las variables (locales o de clase), parámetro final si realmente lo son.
Aunque esto hace que el código sea mucho más detallado, esto ayuda a la lectura / captación fácil del código y también evita los errores ya que la intención está claramente marcada.
¿Qué piensas sobre esto y qué sigues?
Configuré Eclipse para agregar final en todos los campos y atributos que no se modifican. Esto funciona muy bien usando las "acciones de guardado" de Eclipse, que agrega estos modificadores finales (entre otras cosas) al guardar el archivo.
Muy recomendable.
Echa un vistazo a la publicación de mi blog sobre Eclipse Save Actions.
Efectivo Java tiene un elemento que dice "Favorecer objetos inmutables". Declarar los campos como definitivos lo ayuda a dar algunos pequeños pasos hacia esto, pero por supuesto hay mucho más para objetos verdaderamente inmutables que eso.
Si sabe que los objetos son inmutables, se pueden compartir para leer entre muchos hilos / clientes sin preocupaciones de sincronización, y es más fácil razonar sobre cómo se ejecuta el programa.
El modificador final
, especialmente para las variables, es un medio para que el compilador aplique una convención que sea generalmente sensata: asegúrese de que una variable (local o de instancia) se asigne exactamente una vez (ni más ni menos). Al asegurarse de que una variable se asigna definitivamente antes de ser utilizada, puede evitar casos comunes de una NullPointerException
:
final FileInputStream in;
if(test)
in = new FileInputStream("foo.txt");
else
System.out.println("test failed");
in.read(); // Compiler error because variable ''in'' might be unassigned
Al evitar que una variable se asigne más de una vez, desaconseja el alcance excesivo. En lugar de esto:
String msg = null;
for(int i = 0; i < 10; i++) {
msg = "We are at position " + i;
System.out.println(msg);
}
msg = null;
Le recomendamos usar esto:
for(int i = 0; i < 10; i++) {
final String msg = "We are at position " + i;
System.out.println(msg);
}
Algunos enlaces:
- La historia final (capítulo libre del libro "Hardcore Java")
- Algunos patrones finales
- Asignación definitiva
Elegir escribir el final
para cada parámetro en cada método producirá mucha irritación tanto para los codificadores como para los lectores de códigos.
Una vez que la irritación va más allá del cambio razonable a Scala, donde los argumentos son finales por defecto.
O bien, siempre puede usar herramientas de diseño de código que lo harán automáticamente. Todos los IDEs los tienen implementados o como complementos.
Estuve codificando desde hace un tiempo y uso la final siempre que puedo. Después de hacer esto por un tiempo (para variables, parámetros de método y atributos de clase), puedo decir que el 90% (o más) de mis variables son en realidad finales. Creo que el beneficio de NO tener variables modificadas cuando usted no quiere (vi eso antes y es una pena a veces) paga la escritura adicional y las palabras clave adicionales "finales" en su código.
Dicho esto, si diseñara un lenguaje, haría que cada variable sea definitiva a menos que se modifique con alguna otra palabra clave.
No uso mucho para clases y métodos, pensó. Esta es una opción de diseño más o menos complicada, a menos que su clase sea una clase de utilidad (en cuyo caso debe tener solo un constructor privado).
También uso Collections.unmodifiable ... para crear listas no modificables cuando lo necesito.
Final cuando se usa con variables en Java proporciona un sustituto para la constante en C ++. Entonces cuando se usa final y estático para una variable, se vuelve inmutable. Al mismo tiempo, hace que los programadores migrados de C ++ sean bastante felices ;-)
Cuando se usa con variables de referencia, no le permite re-referenciar el objeto, aunque el objeto puede ser manipulado.
Cuando final se usa con un método, no permite que las subclases anulen el método.
Una vez que el uso es muy claro, debe usarse con cuidado. Principalmente depende del diseño, ya que utilizar el método final no ayudaría al polimorfismo.
Uno solo debería usarlo para las variables cuando esté absolutamente seguro de que el valor de la variable nunca se cambiará. También asegúrese de seguir la convención de codificación alentada por SUN. Por ejemplo: final int COLOR_RED = 1; (Mayúscula separada por guion bajo)
Con una variable de referencia, úsela solo cuando necesitemos una referencia inmutable a un objeto en particular.
Con respecto a la parte de legibilidad, los comentarios juegan un papel muy importante cuando se utiliza el modificador final.
Final siempre debe usarse para constantes. Incluso es útil para variables de corta duración (dentro de un único método) cuando las reglas para definir la variable son complicadas.
Por ejemplo:
final int foo;
if (a)
foo = 1;
else if (b)
foo = 2;
else if (c)
foo = 3;
if (d) // Compile error: forgot the ''else''
foo = 4;
else
foo = -1;
Incluso para las variables locales, saber que se declaró final significa que no tengo que preocuparme de que la referencia se modifique más adelante. Esto significa que cuando se depura y veo esa variable más adelante, confío en que se está refiriendo al mismo objeto. Esa es una cosa menos de la que tengo que preocuparme cuando busco un error. Una ventaja es que si el 99% de las variables se declaran definitivas, las pocas variables que realmente son variables se destacan mejor. Además, la final permite al compilador encontrar algunos errores estúpidos más que de otra manera pasarían desapercibidos.
Lo uso para constantes dentro y fuera de métodos.
A veces solo lo uso para métodos porque no sé si una subclase NO quiere anular un método dado (por las razones que sean).
En cuanto a las clases, solo para algunas clases de infraestructura, he usado la clase final.
IntelliJ IDEA le advierte si un parámetro de función está escrito dentro de una función. Por lo tanto, he dejado de usar argumentos finales para funciones. No los veo dentro de la biblioteca Java Runtime también.
Marcar la clase final también puede hacer que algunos enlaces de métodos ocurran en tiempo de compilación en lugar de en tiempo de ejecución. Considere "v2.foo ()" a continuación: el compilador sabe que B no puede tener una subclase, por lo que foo () no puede anularse, por lo que la implementación para llamar se conoce en tiempo de compilación. Si la clase B NO está marcada como final, entonces es posible que el tipo real de v2 sea alguna clase que amplíe B y anule a foo ().
class A {
void foo() {
//do something
}
}
final class B extends A {
void foo() {
}
}
class Test {
public void t(A v1, B v2) {
v1.foo();
v2.foo();
}
}
Nunca he estado en una situación en la que tener una palabra clave final sobre una variable me haya impedido cometer un error, por lo que, por el momento, creo que es una gran pérdida de tiempo.
A menos que exista una razón real para hacerlo (ya que desea hacer que un punto específico sea definitivo), preferiría no hacerlo ya que creo que hace que el código sea menos legible.
Sin embargo, si no encuentra que hace que el código sea más difícil de leer o más largo de escribir, entonces, por supuesto, adelante.
Editar: Como una aclaración (y un intento de recuperar los votos negativos), no estoy diciendo que no marque las constantes como definitivas, sino que digo que no haga cosas como estas:
public String doSomething() {
final String first = someReallyComplicatedExpressionToGetTheString();
final String second = anotherReallyComplicatedExpressionToGetAnother();
return first+second;
}
Simplemente hace que el código (en mi opinión) sea más difícil de leer.
También vale la pena recordar que todas las copias finales te impiden reasignar una variable, no la hace inmutable ni nada por el estilo.
Nunca los uso en variables locales, no tiene mucho sentido la verbosidad añadida. Incluso si no cree que la variable deba ser reasignada, eso cambiará poco la próxima persona que altere ese código que piense lo contrario, y dado que el código está siendo modificado, cualquier propósito original para hacerlo definitivo puede que ya no sea válido. Si solo es por claridad, creo que falla debido a los efectos negativos de la verbosidad.
Más o menos lo mismo se aplica a las variables miembro también, ya que proporcionan pocos beneficios, excepto en el caso de las constantes.
Tampoco influye en la inmutabilidad, ya que el mejor indicador de que algo es inmutable es que está documentado como tal y / o no tiene métodos que puedan alterar el objeto (esto, junto con hacer que la clase sea definitiva, es la única forma de garantizar que es inmutable).
Pero hey, esa es solo mi opinión :-)
Obsesionarse con:
- Campos finales: Marcar campos como finales los fuerza a establecerse para el final de la construcción, lo que hace que la referencia de campo sea inmutable. Esto permite la publicación segura de los campos y puede evitar la necesidad de sincronización en lecturas posteriores. (Tenga en cuenta que para una referencia de objeto, solo la referencia de campo es inmutable; las cosas a las que hace referencia el objeto pueden cambiar y eso afecta la inmutabilidad).
- Campos estáticos finales: aunque ahora utilizo las enumeraciones para muchos de los casos en los que solía usar campos finales estáticos.
Considere pero use juiciosamente:
- Clases finales: el diseño de Framework / API es el único caso donde lo considero.
- Métodos finales: básicamente lo mismo que las clases finales. Si está utilizando patrones de métodos de plantilla como locos y marcando cosas finales, probablemente dependa demasiado de la herencia y no lo suficiente en la delegación.
Ignore a menos que sienta anal:
- Parámetros del método y variables locales: RAREAMENTE hago esto en gran parte porque soy flojo y me parece que desordena el código. Admitiré por completo que marcar los parámetros y las variables locales que no voy a modificar es "correcto". Desearía que fuera el predeterminado. Pero no es así y el código me resulta más difícil de entender con las finales por todas partes. Si estoy en el código de otra persona, no voy a sacarlos, pero si estoy escribiendo un nuevo código, no los incluiré. Una excepción es el caso en el que debe marcar algo final para que pueda acceder desde dentro de una clase interna anónima.
Otra advertencia es que mucha gente confunde final para significar que el contenido de la variable de instancia no puede cambiar, en lugar de que la referencia no pueda cambiar.
Para argumentos, creo que no son necesarios. Mostley ellos solo lastiman la readaptación. Rreasignar una variable de argumento es tan increíblemente estúpido que debería estar seguro de que pueden tratarse como constantes de todos modos.
El hecho de que Eclipse coloree el rojo final hace que sea más fácil detectar las declaraciones de variables en el código, lo que creo que mejora la lectura la mayoría de las veces.
Intento hacer cumplir la regla de que todas las variables deben ser definitivas, no existe una razón muy válida para no hacerlo. Es mucho más fácil responder al "¿qué es esta variable?" pregunta si solo tienes que encontrar la iniciación y estar seguro de que eso es todo.
De hecho, me pongo bastante nervioso con las variables no finales ahora. Es como la diferencia entre tener un cuchillo colgando de un hilo sobre tu cabeza, o simplemente tener tu cajón de cocina ...
Una variable final es simplemente una buena forma de los valores de la etiqueta.
Una variable no final está vinculada a parte de algún algoritmo propenso a errores.
Una buena característica es que cuando la opción de usar una variable fuera de la pregunta para un algoritmo la mayoría de las veces la solución es escribir un método, lo que generalmente mejora el código significativamente.
Parece que uno de los mayores argumentos en contra del uso de la palabra clave final es que "es innecesario" y "desperdicia espacio".
Si reconocemos los muchos beneficios de "final" como lo señalan muchos grandes posts aquí, aunque admito que requiere más tipeo y espacio, yo argumentaría que Java debería haber hecho variables "definitivas" por defecto, y requiere que las cosas estén marcadas " mutable "si el codificador quiere que sea.
Realmente necesita comprender el uso completo de la palabra clave final antes de usarla. Se puede aplicar ay tiene diferentes efectos en variables, campos, métodos y clases
Recomiendo consultar el artículo vinculado a continuación para obtener más detalles.
Se recomienda encarecidamente usar el final para las constantes . Sin embargo, no lo usaría para métodos o clases (o al menos pensarlo por un tiempo), porque hace que las pruebas sean más difíciles, si no imposibles. Si definitivamente debe hacer que una clase o método sea definitivo, asegúrese de que esta clase implemente alguna interfaz, de modo que pueda tener un simulacro que implemente la misma interfaz.
Soy bastante dogmático sobre declarar todas las variables posibles como final
. Esto incluye parámetros de método, variables locales y, raramente, campos de objetos de valor. Tengo tres razones principales para declarar las variables finales en todas partes:
- Declaración de Intención: Al declarar una variable final, afirmo que esta variable debe escribirse solo una vez. Es una sugerencia sutil para otros desarrolladores y una gran pista para el compilador.
- Aplicación de variables de uso único: Creo en la idea de que cada variable debe tener un solo propósito en la vida. Al asignarle a cada variable un solo propósito, se reduce el tiempo que lleva asimilar el propósito de esa variable en particular mientras se depura.
- Permite la optimización: sé que el compilador solía tener trucos de mejora del rendimiento que se basaban específicamente en la inmutabilidad de una referencia de variable. Me gusta pensar que algunos de estos viejos trucos de rendimiento (o nuevos) serán utilizados por el compilador.
Sin embargo, creo que las clases y los métodos finales no son tan útiles como las referencias de variables finales. La palabra clave final
, cuando se usa con estas declaraciones, simplemente proporciona controles de ruta para las pruebas automatizadas y el uso de su código en formas que nunca podría haber anticipado.
Tengo que leer un montón de código para mi trabajo. La ausencia de las variables de instancia es una de las principales cosas que me molestan y hace que entender el código sea innecesariamente difícil. Por mi dinero, el final en las variables locales causa más desorden que claridad. El lenguaje debería haber sido diseñado para que sea el predeterminado, pero tenemos que vivir con el error. Algunas veces es útil particularmente con bucles y asignaciones definidas con un árbol if-else, pero principalmente tiende a indicar que su método es demasiado complicado.
Usar clases locales anónimas para los detectores de eventos y tal es un patrón común en Java. El uso más común de la palabra clave final es asegurarse de que las variables en el alcance sean accesibles para el oyente par.
Sin embargo, si se ve obligado a poner muchas declaraciones finales en su código. Esa podría ser una buena pista de que estás haciendo algo mal.
El artículo publicado arriba da este ejemplo:
public void doSomething(int i, int j) {
final int n = i + j; // must be declared final
Comparator comp = new Comparator() {
public int compare(Object left, Object right) {
return n; // return copy of a local variable
}
};
}
Uso final
todo el tiempo para los atributos del objeto.
La palabra clave final
tiene semántica de visibilidad cuando se usa en atributos de objetos. Básicamente, establecer el valor de un atributo de objeto final ocurre antes de que el constructor regrese. Esto significa que siempre que no permita que this
referencia escape del constructor y use la final
para todos sus atributos, su objeto está (bajo la semántica de Java 5) garantizado para ser construido adecuadamente, y dado que también es inmutable, puede ser publicado de forma segura a otros hilos.
Los objetos inmutables no se trata solo de seguridad de hilos. También hacen que sea mucho más fácil razonar acerca de las transiciones de estado en su programa, porque el espacio de lo que puede cambiar es deliberadamente y, si se usa de manera coherente, se limita por completo a solo las cosas que deberían cambiar.
A veces también hago que los métodos sean definitivos, pero no tan a menudo. Casi nunca hago clases finales. Generalmente hago esto porque tengo poca necesidad de hacerlo. Generalmente no uso mucho la herencia. Prefiero usar interfaces y composición de objetos en su lugar, esto también se presta a un diseño que a menudo resulta más fácil de probar. Cuando codifica interfaces en lugar de clases concretas, no necesita usar herencia cuando prueba, tal como está, con frameworks como jMock, mucho más fácil crear objetos simulados con interfaces que con clases concretas.
Supongo que debería hacer que la mayoría de mis clases sean finales, pero aún no me he metido en el habito.
final obviamente se debe usar en constantes y para forzar la inmutabilidad, pero hay otro uso importante en los métodos.
Efectivo Java tiene un ítem completo sobre esto (Ítem 15) que señala los peligros de la herencia involuntaria. Efectivamente, si no diseñó y documenta su clase para heredar, heredar puede generar problemas inesperados (el elemento da un buen ejemplo). La recomendación, por lo tanto, es que use el final en cualquier clase y / o método del que no se haya querido heredar.
Eso puede parecer draconiano, pero tiene sentido. Si está escribiendo una biblioteca de clase para que otros la utilicen, entonces no quiere que herede cosas que no fueron diseñadas para ello: se encerrará en una implementación particular de la clase para compatibilidad con la parte posterior. Si está codificando en un equipo, no hay nada que impida que otro miembro del equipo elimine la final si es que realmente tiene que hacerlo. Pero la palabra clave les hace pensar en lo que están haciendo y les advierte que la clase de la que están heredando no está diseñada para ello, por lo que deben tener mucho cuidado.
Creo que todo tiene que ver con un buen estilo de codificación. Por supuesto, puedes escribir programas buenos y sólidos sin usar muchos modificadores final
ningún lado, pero cuando lo piensas ...
Agregar final
a todas las cosas que no debería cambiar simplemente reduce las posibilidades de que usted (o el próximo programador, trabajando en su código) malinterprete o haga un mal uso del proceso de pensamiento que dio como resultado su código. Al menos debería sonar algunas campanas cuando ahora quieren cambiar tu cosa previamente inmutable.
Al principio, parece incómodo ver muchas palabras clave final
en tu código, pero muy pronto dejarás de notar la palabra misma y simplemente pensarás, eso-cosa-nunca-cambiará-de-este-punto -on (puedes quitármelo ;-)
Creo que es una buena práctica. No lo estoy usando todo el tiempo, pero cuando puedo y tiene sentido etiquetar algo final
lo haré.