Usar el modificador "final" cuando sea aplicable en Java [cerrado]


194

En Java, existe la práctica de declarar cada variable (local o clase), parámetro final si realmente lo son.

Aunque esto hace que el código sea mucho más detallado, esto ayuda a una fácil lectura / comprensión del código y también evita errores ya que la intención está claramente marcada.

¿Qué piensa sobre esto y qué sigue?


13
Esto puede llevar a una discusión religiosa. A algunas personas les gusta, algunas personas lo odian. Me gustan los campos finales pero no las variables locales finales a menos que deban serlo, no estoy seguro de que sea completamente racional. No estoy seguro de que cualquier voto negativo sea tampoco. Estoy de acuerdo con Alex Miller. ;)
Peter Lawrey

Puedo entender si a la gente no le gusta tener su código lleno de finales. Pero este es un problema que un buen editor podría resolver: bugs.eclipse.org/bugs/show_bug.cgi?id=409379
oberlies

Respuestas:


184

Creo que todo tiene que ver con un buen estilo de codificación. Por supuesto, puedes escribir programas buenos y robustos sin usar muchos finalmodificadores en ningún lado, pero cuando lo piensas ...

Agregar finala todas las cosas que no deberían cambiar, simplemente reduce las posibilidades de que usted (o el próximo programador, trabajando en su código) malinterprete o use mal el proceso de pensamiento que resultó en su código. Al menos debería sonar algunas campanas cuando ahora quieren cambiar tu cosa inmutable anterior.

Al principio, parece un poco incómodo ver muchas finalpalabras clave en su código, pero muy pronto dejará de notar la palabra en sí y simplemente pensará, esa-cosa-nunca-cambiará-a partir de este punto- encendido (me lo puedes quitar ;-)

Creo que es una buena práctica. No lo uso todo el tiempo, pero cuando pueda y tenga sentido etiquetar algo, finallo haré.


16
En lugar de usar la palabra clave final para los parámetros, puede usar una herramienta de análisis estático como FindBugs para requerir que las variables de parámetros no se reasignen. De esta forma, elimina la carga sintáctica y puede aplicarla mediante una comprobación FindBugs en su herramienta de integración continua.
Timo Westkämper

20
@Timo Esto funcionaría, pero tiene la desventaja de que esto debe verificarse después del registro y no mientras el desarrollador está trabajando en el código. Porque final, obviamente, el compilador no te permitirá continuar si cometes un error.
Mike

9
Eclipse tiene una opción para agregar finalsiempre que sea posible (variables que no cambian) cada vez que guarde.
Bert F

22
+1 me encanta final. No hace la diferencia el 98% del tiempo, es un inconveniente menor% 1 del tiempo, pero el 1% del tiempo me salva de hacer algo estúpido o involuntario que bien vale la pena.
Bert F

14
@BertF Sí, siempre dejo que Eclipse agregue finalmodificadores en todas partes cuando "Limpio ..." mi código. En todas partes significa incluso en bloques catch (), y como se dijo, no los notará después de un tiempo. Por supuesto, si creara un idioma, el finalsería el predeterminado y a varo modifiablesería la palabra clave opcional.
Maarten Bodewes

191

Obsesionarse con:

  • Campos finales: marcar campos como finales los obliga a establecerse al final de la construcción, haciendo que la referencia de campo sea inmutable. Esto permite la publicación segura de 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 se refiere la referencia de objeto todavía pueden cambiar y eso afecta la inmutabilidad).
  • Campos estáticos finales: aunque ahora uso enumeraciones para muchos de los casos en los que solía usar campos estáticos finales.

Considere pero use juiciosamente:

  • Clases finales: el diseño de marco / API es el único caso en el que lo considero.
  • Métodos finales: básicamente lo mismo que las clases finales. Si está utilizando patrones de métodos de plantilla como loco y marcando cosas finales, probablemente esté confiando demasiado en la herencia y no lo suficiente en la delegación.

Ignorar a menos que se sienta anal:

  • Parámetros del método y variables locales: RARAMENTE hago esto en gran medida porque soy flojo y me parece que satura el código. Admitiré completamente que marcar parámetros y variables locales que no voy a modificar es "más correcto". Desearía que fuera el valor predeterminado. Pero no lo es y encuentro que el código es más difícil de entender con finales por todas partes. Si estoy en el código de otra persona, no voy a extraerlo, pero si escribo un código nuevo no lo pondré. 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.

8
mejor respuesta hasta ahora en múltiples preguntas sobre este tema final
Gabriel Ščerbák

44
La mayoría de las veces cuando veo la variable local sin la palabra final delante de ella y no se puede usar allí, me dice que probablemente debería extraer algo de código en un nuevo método que devuelva el valor deseado que haré final. Los casos en los que esto no es aplicable es cuando uso algunas transmisiones o de lo contrario hay que intentar atraparlo.
nyxz

32

Realmente necesita comprender el uso completo de la palabra clave final antes de usarla. Se puede aplicar y tiene diferentes efectos en variables, campos, métodos y clases.

Recomiendo consultar el artículo vinculado a continuación para obtener más detalles.

Palabra final sobre la palabra clave final


29

El finalmodificador, especialmente para las variables, es un medio para que el compilador haga cumplir una convención que generalmente es 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 asigne definitivamente antes de usarla, puede evitar casos comunes de 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, desalienta 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;

Se le recomienda usar esto:

 for(int i = 0; i < 10; i++) {
     final String msg = "We are at position " + i;
     System.out.println(msg);
 }

Algunos enlaces:


1
+1 para disuadir el argumento de alcance excesivo
Tom Tresansky

En resumen, básicamente puedes usar finalpara hacer Java más basado en expresiones .
Adam Gent

En realidad "Al asegurarse de que una variable esté definitivamente asignada antes de usarla, puede evitar casos comunes de una NullPointerException:" esto no es correcto, 'final' no hace ninguna diferencia aquí, el compilador sabe y posiblemente se queja de la variable 'in' siendo nulo en el ejemplo proporcionado.
nyholku

@nyholku Tienes razón, en Java las variables locales deben asignarse definitivamente antes de su uso, también sin final. Pero para los campos necesitas la final. En un ejemplo un poco más complejo donde "in" es una variable de instancia de una clase, y if / else está en el constructor, necesita el final para señalar el problema. Además, también para las variables locales hay algún valor en la IMO final, ya que evita que el programador descuidado 'arregle' el error de compilación sobre "in" posiblemente no asignado agregando "= nulo" a su declaración. En otras palabras, las variables finales reducen los usos de nulo, en mi experiencia.
Bruno De Fraine 01 de

20

Soy bastante dogmático acerca de declarar todas las variables posibles final. Esto incluye parámetros de métodos, variables locales y, rara vez, campos de objetos de valor. Tengo tres razones principales para declarar variables finales en todas partes:

  1. Declaración de intención: Al declarar una variable final, estoy afirmando que esta variable debe escribirse solo una vez. Es una pista sutil para otros desarrolladores, y una gran pista para el compilador.
  2. Hacer cumplir las variables de un solo uso: creo en la idea de que cada variable debe tener un solo propósito en la vida. Al darle a cada variable un solo propósito, reduce el tiempo que se tarda en asimilar el propósito de esa variable en particular durante la depuración.
  3. 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 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 métodos finales no son tan útiles como las referencias variables finales. La finalpalabra clave, cuando se usa con estas declaraciones, simplemente proporciona obstáculos para las pruebas automatizadas y el uso de su código de formas que nunca podría haber anticipado.


2
finalLas variables y los parámetros del método no tienen impacto en el rendimiento, ya que no se expresa en el código de bytes.
Steve Kuo

variable: = latín: varius> varios> modificable
dieter

17

Java efectivo tiene un elemento que dice "Favorecer objetos inmutables". Declarar los campos como finales 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 problemas de sincronización, y es más fácil razonar sobre cómo se ejecuta el programa.


15
cuidado - final e inmutable son conceptos completamente diferentes. Es fácil tener un objeto mutable final: final se trata de la referencia, la mutabilidad se trata de la instancia del objeto. Persona final olaf = nueva Persona (); olaf.setName ("Olaf");
Olaf Kock

1
Exactamente: los objetos inmutables son una cosa, las referencias inmutables (es decir, finales) son algo completamente diferente.
SCdF

2
Pero están estrechamente relacionados, ya que puede declarar el campo Person.name como final (y declarar la clase final, y ...) hacer que el objeto Person final. Está sin embargo no es tan fácil como simplemente haciendo "Persona final" ...
Sebastián Ganslandt

Esto también es irrelevante para las variables locales (los parámetros son locales). Solo se aplica a las variables de clase, por lo que solo aborda parcialmente la pregunta.
Robin

12

Nunca he estado en una situación en la que tener una palabra clave final en 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 haya una razón real para hacerlo (ya que desea hacer un punto específico sobre que la variable sea final) 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 hágalo.

Editar: Como una aclaración (y un intento de recuperar votos negativos), no digo que no marquen las constantes como finales, digo que no hagan cosas como:

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 todo lo que hace es evitar que reasignes una variable, no la hace inmutable ni nada por el estilo.


1
Volviendo la afirmación: he estado en muchas situaciones en las que no usar final(y objetos inmutables en general) ha tenido un factor importante que contribuye al número y al impacto de los errores.
Chris Vest

3
Estoy a favor de los objetos inmutables, simplemente nunca he estado en una situación en la que marcar un objeto inmutable final me haya ayudado.
SCdF

2
Estoy de acuerdo en que no deberíamos usar final en variables locales y parámetros de métodos, hace que el código sea mucho menos legible.
rmaruszewski

@ChrisVest, quizás tus funciones son demasiado largas
Pacerier

8

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;

6

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 muchas publicaciones excelentes aquí, mientras admitimos que requiere más tipeo y espacio, diría que Java debería haber hecho variables "finales" por defecto, y requeriría que las cosas estén marcadas " mutable "si el codificador quiere que sea.


1
Votantes, ¿te gustaría explicar?
RAYO

Parece extraño llamarlo variable entonces, ¿no?
Alan

2
Hay miles de palabras en inglés para elegir.
RAYO

Esto es perfecto. No hay argumentos en contra finalaparte de "demasiado largo para escribir", "hace que el código sea más torpe". Hay varios argumentos para final. Y podemos agregarlo automáticamente al guardar, si no desea escribirlo a mano.
Dmitriy Popov

5

Yo uso finaltodo el tiempo para los atributos del objeto.

La finalpalabra clave tiene semántica de visibilidad cuando se usa en atributos de objeto. Básicamente, establecer el valor de un atributo de objeto final ocurre antes de que regrese el constructor. Esto significa que, siempre y cuando no permita que la thisreferencia escape del constructor y la utilice finalpara todos sus atributos, su objeto (bajo la semántica de Java 5) está garantizado para ser construido adecuadamente, y dado que también es inmutable, puede publicarse de manera segura a otros hilos.

Los objetos inmutables no se trata solo de la seguridad del hilo. También hacen que sea mucho más fácil razonar sobre las transiciones de estado en su programa, porque el espacio de lo que puede cambiar es deliberado y, si se usa de manera consistente, se limita completamente a solo las cosas que deberían cambiar.

A veces también hago que los métodos sean finales, pero no con tanta frecuencia. Rara vez hago las 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, esto también se presta a un diseño que a menudo es más fácil de probar. Cuando codifica interfaces en lugar de clases concretas, no necesita usar la herencia cuando prueba, como es, con marcos 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 todavía no he entrado en el habitáculo.


4

Tengo que leer mucho código para mi trabajo. Perder variables finales en la instancia es una de las principales cosas que me molestan y hace que la comprensión del código sea innecesariamente difícil. Para mi dinero, las variables locales finales causan 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. A veces es útil particularmente con bucles y asignación definida con un árbol if-else, pero en su mayoría tiende a indicar que su método es demasiado complicado.


¿Por qué no usar una herramienta que reduce el desorden?
Pacerier

@Pacerier ¿Como en un editor que tergiversa el código?
Tom Hawtin - tackline

3

Obviamente, final debe usarse en constantes y para imponer la inmutabilidad, pero hay otro uso importante en los métodos.

Java efectivo tiene un ítem completo sobre esto (Ítem 15) que señala las trampas de la herencia involuntaria. Efectivamente, si no diseñó y documentó su clase para la herencia, heredar de ella puede dar problemas inesperados (el elemento es un buen ejemplo). Por lo tanto, la recomendación es que use final en cualquier clase y / o método del que no haya sido heredado.

Eso puede parecer draconiano, pero tiene sentido. Si está escribiendo una biblioteca de clases para que otros la usen, entonces no quiere que hereden de cosas que no fueron diseñadas para ello; se encerrará en una implementación particular de la clase para la compatibilidad con versiones anteriores. Si está codificando en un equipo, no hay nada que impida que otro miembro del equipo elimine la final si realmente tiene que hacerlo. Pero la palabra clave los hace pensar en lo que están haciendo y les advierte que la clase de la que heredan no fue diseñada para eso, por lo que deben tener mucho cuidado.


3

Otra advertencia es que muchas personas confunden final para significar que el contenido de la variable de instancia no puede cambiar, en lugar de que la referencia no puede cambiar.


Y esta publicación es una gran prueba de eso.
inigoD

2

Incluso para las variables locales, saber que se declara final significa que no tengo que preocuparme de que la referencia se cambie más adelante. Esto significa que cuando depuro y veo esa variable más adelante, estoy seguro de que se está refiriendo al mismo objeto. Esa es una cosa menos de la que debo preocuparme cuando busco un error. Una ventaja es que si el 99% de las variables se declaran finales, entonces las pocas variables que realmente son variables se destacan mejor. Además, la final le permite al compilador encontrar algunos errores estúpidos más posibles que de otro modo podrían pasar desapercibidos.


2

Elegir escribir finalpara 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á de lo razonable, cambie 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 por usted. Todos los IDE los tienen implementados o como complementos.


1

Final cuando se usa con variables en Java proporciona un sustituto de constante en C ++. Entonces, cuando se usa final y static para una variable, se vuelve inmutable. Al mismo tiempo, hace que los programadores migrados de C ++ estén muy contentos ;-)

Cuando se usa con variables de referencia, no le permite volver a hacer referencia al objeto, aunque el objeto puede ser manipulado.

Cuando final se utiliza 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. Depende principalmente del diseño, ya que el uso de final en el método no ayudaría al polimorfismo.

Uno solo debería usarlo para variables cuando esté absolutamente seguro de que el valor de la variable nunca / deberá cambiarse. También asegúrese de seguir la convención de codificación promovida por SUN, por ejemplo: final int COLOR_RED = 1; (Mayúsculas separadas por guiones bajos)

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 usa el modificador final.


¿Alguien puede decirme por qué esto se rechaza? Solo curioso ..
Omnipotente

Probablemente sea porque declaras que SOLO deberías hacerlo final cuando XYZ. Otros consideran que es una mejor práctica hacer que TODO sea definitivo, a menos que sea necesario hacerlo de otra manera.
Outlaw Programmer

1
No es en absoluto un sustituto de la palabra clave const de C ++. -1.
tmj

1

Nunca los uso en variables locales, no tiene mucho sentido la verbosidad añadida. Incluso si no cree que la variable deba reasignarse, eso hará poca diferencia para la próxima persona que altere ese código que piensa lo contrario, y dado que el código se está cambiando, cualquier propósito original para hacerlo definitivo ya no será válido. Si es solo por claridad, creo que falla debido a los efectos negativos de la verbosidad.

Casi lo mismo se aplica también a las variables miembro, ya que proporcionan poco beneficio, excepto en el caso de las constantes.

Tampoco tiene relación con 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 final sea la única forma de garantizar que Es inmutable).

Pero bueno, esa es solo mi opinión :-)


1

Configuré Eclipse para agregar final en todos los campos y atributos que no se modifican. Esto funciona muy bien usando las "acciones de guardar" de Eclipse que agrega estos modificadores finales (entre otras cosas) al guardar el archivo.

Muy recomendable.

Echa un vistazo a mi blog de Eclipse Save Actions


1

Para argumentos, creo que no son necesarios. Mostley simplemente lastiman la legibilidad. Firmar una variable de argumento es tan increíblemente estúpido que debería estar bastante seguro de que de todos modos pueden tratarse como constantes.

El hecho de que Eclipse colorea el rojo final hace que sea más fácil detectar declaraciones variables en el código, lo que creo que mejora la readbillity la mayor parte del tiempo.

Intento hacer cumplir la regla de que todas y cada una de las variables deberían ser finales si no hay una razón extremadamente válida para no hacerlo. Es mucho más fácil responder "¿qué es esta variable?" pregunta si solo tienes que encontrar la inicialización y estar seguro de que es así.

De hecho, me pongo bastante nervioso por las variables no finales hoy en día. Es como la diferencia entre tener un cuchillo colgando de un hilo sobre tu cabeza, o simplemente tenerlo en el cajón de la cocina ...

Una variable final es solo una buena forma de etiquetar valores.

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, que generalmente mejora el código significativamente.


1

He estado codificando durante un tiempo y usando final siempre que puedo. Después de hacer esto por un tiempo (para variables, parámetros de métodos y atributos de clase), puedo decir que el 90% (o más) de mis variables son realmente finales. Creo que el beneficio de NO tener variables modificadas cuando no quieres (lo vi antes y es un dolor a veces) paga por el tipeo adicional y las palabras clave "finales" adicionales en tu código.

Dicho esto, si diseñara un lenguaje, haría que todas las variables fueran finales a menos que sean modificadas por alguna otra palabra clave.

No uso mucho el final 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 debería tener solo un constructor privado).

También uso Collections.unmodifiable ... para crear listas no modificables cuando sea necesario.


0

El uso de clases locales anónimas para oyentes 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 uniforme.

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 anteriormente 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
        }
    };
}

0

Lo uso para constantes dentro y fuera de los métodos.

Solo lo uso a veces para métodos porque no sé si una subclase NO querría anular un método dado (por alguna razón).

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 se escribe dentro de una función. Entonces, dejé de usar final para argumentos de función. No los veo dentro de la biblioteca Java Runtime también.


0

Apenas uso final en métodos o clases porque me gusta permitir que las personas los anulen.

De lo contrario, solo lo uso finalmente si es un public/private static final type SOME_CONSTANT;


Hmm ... editado en un lugar ... todavía se dice finalmente en la segunda línea ;-)
Omnipotente

Permitir que las personas anulen los valores es la única fuente de sorpresas y errores difíciles de resolver
RAYO del

0

Marcar la clase final también puede hacer que algunos enlaces de métodos sucedan en tiempo de compilación en lugar de 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 se marca como final, entonces es posible que el tipo real de v2 sea una clase que extienda B y anule 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();
    }
}

-1

Se recomienda usar final para 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 absolutamente debe hacer que una clase o método sea final, asegúrese de que esta clase implemente alguna interfaz, para que pueda tener un simulacro que implemente la misma interfaz.


No hace que las pruebas sean más difíciles porque de todos modos debería usar interfaces.
Chris Vest
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.