¿Cuándo se debe usar el final para los parámetros del método y las variables locales?

He encontrado un par de referencias ( por ejemplo ) que sugieren usar el final tanto como sea posible y me pregunto qué tan importante es eso. Esto se debe principalmente al contexto de los parámetros del método y las variables locales, no a los métodos o clases finales. Para las constantes, tiene sentido obvio.

Por un lado, el comstackdor puede hacer algunas optimizaciones y hace que la intención del progtwigdor sea más clara. Por otro lado, agrega verbosidad y las optimizaciones pueden ser triviales.

¿Es algo que debería hacer un esfuerzo para recordar hacer?

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.

¿Es algo que debería hacer un esfuerzo para recordar hacer?

No, si está utilizando Eclipse, porque puede configurar una Acción de guardado para agregar automáticamente estos modificadores finales . Entonces obtienes los beneficios por menos esfuerzo.

Los beneficios de tiempo de desarrollo de “final” son al menos tan significativos como los beneficios de tiempo de ejecución. Le dice a los futuros editores del código algo sobre tus intenciones.

Marcar una clase “final” indica que no ha hecho un esfuerzo durante el diseño o la implementación de la clase para manejar la extensión con elegancia. Si los lectores pueden hacer cambios en la clase y desean eliminar el modificador “final”, pueden hacerlo bajo su propio riesgo. Depende de ellos asegurarse de que la clase maneje bien la extensión.

Marcar una variable “final” (y asignarla en el constructor) es útil con la dependency injection. Indica la naturaleza “colaboradora” de la variable.

Marcar un método “final” es útil en clases abstractas. Delinea claramente dónde están los puntos de extensión.

He encontrado que los parámetros del método de marcado y los locales como final son útiles como ayuda de refactorización cuando el método en cuestión es un lío incomprensible de varias páginas. Espolvoree el final liberalmente, vea qué errores “no puede asignar a la variable final” que el comstackdor (o su IDE) arroja, y usted puede descubrir por qué la variable llamada “datos” termina nula a pesar de que varios comentarios (desactualizados) juran que no puede suceder

Luego puede corregir algunos de los errores al reemplazar las variables reutilizadas con nuevas variables declaradas más cercanas al punto de uso. Luego descubres que puedes envolver partes enteras del método en llaves de scope, y de repente eres una llave IDE alejada de “Método de extracción” y tu monstruo se volvió más comprensible.

Si su método no es ya una ruina imposible de mantener, creo que puede ser valioso hacer cosas definitivas para desalentar a las personas a convertirlas en rests de naufragios; pero si se trata de un método breve (ver: no imposible de mantener), entonces corre el riesgo de agregar mucha verbosidad. ¡En particular, las firmas de función de Java son lo suficientemente difíciles como para caber en 80 caracteres como lo es sin agregar seis más por argumento!

Bueno, todo esto depende de tu estilo … si te gusta ver el final cuando no estarás modificando la variable, entonces úsalo. Si no te gusta verlo … entonces déjalo fuera.

Personalmente, me gusta la menor verborrea posible, por lo que tiendo a evitar el uso de palabras clave adicionales que no son realmente necesarias.

Aunque prefiero los lenguajes dynamics, así que probablemente no sea una sorpresa. Me gusta evitar la verborrea.

Por lo tanto, yo diría que simplemente escoja la dirección hacia la que se inclina y simplemente continúe con ella (en cualquier caso, trate de ser consecuente).


Como nota al margen, he trabajado en proyectos que usan y no usan dicho patrón, y no he visto diferencia en la cantidad de errores o fallas … No creo que sea un patrón que genere un gran impacto. mejore su conteo de errores o cualquier otra cosa, pero nuevamente es estilo, y si le gusta express el bash de que no lo modifique, siga adelante y úselo.

Uso final todo el tiempo para hacer que Java se base más en expresiones. Ver las condiciones de Java ( if,else,switch ) no se basan en expresiones, lo que siempre he odiado, especialmente si está acostumbrado a la progtwigción funcional (es decir, ML, Scala o Lisp).

Por lo tanto, debe intentar siempre (en mi humilde opinión) usar variables finales cuando use condiciones.

Dejame darte un ejemplo:

  final String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; case JOB_POSTING_IMPORT: name = "Blah"; break; default: throw new IllegalStateException(); } 

Ahora si agrega otra statement de case y no establece el name el comstackdor fallará. El comstackdor también fallará si no se divide en todos los casos (que se establece la variable). Esto le permite hacer que Java sea muy similar a las expresiones de let de Lisp y lo hace para que su código no tenga indentaciones masivas (debido a las variables de scope léxico).

Y como notó @Recurse (pero aparentemente -1 me) puedes hacer lo anterior sin hacer que String name final para obtener el error del comstackdor (que nunca dije que no pudieras) pero podrías hacer fácilmente que el error del comstackdor desaparezca. después de la instrucción switch que arroja la semántica de la expresión o, peor aún, se olvida de break y no se puede producir un error (a pesar de lo que dice @Recurse) sin utilizar el final :

  String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; //break; whoops forgot break.. //this will cause a compile error for final ;P @Recurse case JOB_POSTING_IMPORT: name = "Blah"; break; } // code, code, code // Below is not possible with final name = "Whoops bug"; 

Debido al nombre de la configuración del error (además de olvidar break otro error), ahora puedo hacer esto accidentalmente:

  String name; switch(pluginType) { case CANDIDATE_EXPORT: name = "Candidate Stuff"; break; //should have handled all the cases for pluginType } // code, code, code // Below is not possible with final name = "Whoops bug"; 

La variable final obliga a una evaluación única de qué nombre debería ser. Similar a cómo una función que tiene un valor de retorno siempre debe devolver un valor (ignorando excepciones), el bloque de cambio de nombre tendrá que resolver el nombre y, por lo tanto, vincularlo a ese bloque de conmutadores, lo que facilita la refactorización de fragmentos de código (es decir, Eclipe refactor: método de extracción) .

Lo anterior en OCaml:

 type plugin = CandidateExport | JobPostingImport let p = CandidateExport let name = match p with | CandidateExport -> "Candidate Stuff" | JobPostingImport -> "Blah" ;; 

El match ... with ... evalúa como una función, es decir, expresión. Observe cómo se ve nuestra statement de cambio.

Aquí hay un ejemplo en Scheme (Racket or Chicken):

 (define name (match b ['CandidateExport "Candidate Stuff"] ['JobPostingImport "Blah"])) 

Si está escribiendo una aplicación que alguien tendrá que leer el código después de, digamos, 1 año, entonces sí, use la variable final que no se debe modificar todo el tiempo. Al hacer esto, su código será más “auto-documentado” y también reducirá la posibilidad de que otros desarrolladores hagan cosas tontas como usar una constante local como una variable temporal local.

Si estás escribiendo un código desechable, entonces, no, no te molestes en identificar todas las constantes y hacer que sean definitivas.

Es útil en los parámetros para evitar cambiar el valor del parámetro por accidente e introducir un error sutil. Utilizo para ignorar esta recomendación pero después de pasar unas 4 horas. en un método horrible (con cientos de líneas de código y múltiples fors, if nesteds y todo tipo de malas prácticas) te recomendaría que lo hicieras.

  public int processSomethingCritical( final int x, final int y ){ // hundreds of lines here // for loop here... int x2 = 0; x++; // bug aarrgg... // hundreds of lines there // if( x == 0 ) { ... } 

Por supuesto, en un mundo perfecto esto no sucedería, pero … bueno … a veces tienes que apoyar el código de otros. 🙁

Usaré el final tanto como pueda. Si lo hace, se marcará si cambia involuntariamente el campo. También establecí los parámetros del Método en final. Al hacerlo, he capturado varios errores del código que he tomado cuando intentan ‘establecer’ un parámetro olvidando que Java pasa por valor.

No queda claro a partir de la pregunta si esto es obvio, pero hacer que un parámetro de método final afecte solo al cuerpo del método. NO transmite ninguna información interesante sobre las intenciones del método al invocador. El objeto que se está pasando todavía puede mutarse dentro del método (las finales no son constelaciones), y el scope de la variable está dentro del método.

Para responder a su pregunta precisa, no me molestaría en convertir una instancia o variable local (incluidos los parámetros del método) en definitiva a menos que el código lo requiera (p. Ej., Se hace referencia a la variable desde una clase interna) o aclarar alguna lógica realmente complicada.

Por ejemplo, las variables, las convertiría en definitivas si son lógicamente constantes.

Hay muchos usos para la variable final . Aquí hay solo algunos

Constantes finales

  public static class CircleToolsBetter { public final static double PI = 3.141; public double getCircleArea(final double radius) { return (Math.pow(radius, 2) * PI); } } 

Esto se puede usar luego para otras partes de sus códigos, o para acceder a otras clases, de esa manera si alguna vez cambia el valor, no tendría que cambiarlos uno por uno.

Variables finales

 public static String someMethod(final String environmentKey) { final String key = "env." + environmentKey; System.out.println("Key is: " + key); return (System.getProperty(key)); } } 

En esta clase, crea una variable final con ámbito que agrega un prefijo al parámetro environmentKey. En este caso, la variable final es final solo dentro del scope de la ejecución, que es diferente en cada ejecución del método. Cada vez que se ingresa el método, se reconstruye el final. Tan pronto como se construye, no se puede cambiar durante el scope de la ejecución del método. Esto le permite corregir una variable en un método por la duración del método. vea abajo:

 public class FinalVariables { public final static void main(final String[] args) { System.out.println("Note how the key variable is changed."); someMethod("JAVA_HOME"); someMethod("ANT_HOME"); } } 

Constantes finales

 public double equation2Better(final double inputValue) { final double K = 1.414; final double X = 45.0; double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M); double powInputValue = 0; if (result > 360) { powInputValue = X * Math.sin(result); } else { inputValue = K * Math.sin(result); // <= Compiler error } 

Estos son especialmente útiles cuando tiene líneas muy largas de códigos, y generará un error de comstackción para que no se ejecute en un error lógico / comercial cuando alguien accidentalmente cambie variables que no deberían modificarse.

Colecciones finales

Caso diferente cuando hablamos de Colecciones, necesitas establecerlas como no modificables.

  public final static Set VALID_COLORS; static { Set temp = new HashSet( ); temp.add(Color.red); temp.add(Color.orange); temp.add(Color.yellow); temp.add(Color.green); temp.add(Color.blue); temp.add(Color.decode("#4B0082")); // indigo temp.add(Color.decode("#8A2BE2")); // violet VALID_COLORS = Collections.unmodifiableSet(temp); } 

de lo contrario, si no lo configura como no modificable:

 Set colors = Rainbow.VALID_COLORS; colors.add(Color.black); // <= logic error but allowed by compiler 

Las clases finales y los métodos finales no se pueden extender o sobrescribir, respectivamente.

EDITAR: PARA ABORDAR EL PROBLEMA DE LA CLASE FINAL EN RELACIÓN CON LA ENCAPSULACIÓN:

Hay dos formas de hacer una clase final. El primero es usar la palabra clave final en la statement de clase:

 public final class SomeClass { // . . . Class contents } 

La segunda forma de hacer una clase final es declarar todos sus constructores como privados:

 public class SomeClass { public final static SOME_INSTANCE = new SomeClass(5); private SomeClass(final int value) { } 

Marcarlo final le ahorra la molestia si descubre que es real una final, para demostrar que mira esta clase de prueba. se ve público a primera vista.

 public class Test{ private Test(Class beanClass, Class stopClass, int flags) throws Exception{ // . . . snip . . . } } 

Desafortunadamente, dado que el único constructor de la clase es privado, es imposible extender esta clase. En el caso de la clase de prueba, no hay ninguna razón para que la clase sea final. La clase de prueba es un buen ejemplo de cómo las clases finales implícitas pueden causar problemas.

Por lo tanto, debe marcarlo como definitivo cuando implícitamente haga una clase final haciendo que su constructor sea privado.

Un poco de una compensación como usted menciona, pero prefiero el uso explícito de algo más que el uso implícito. Esto ayudará a eliminar cierta ambigüedad para los futuros mantenedores de código, incluso si es solo usted.

Si tiene clases internas (anónimas) y el método necesita acceder a la variable del método contenedor, necesita tener esa variable como definitiva.

Aparte de eso, lo que has dicho es correcto.

Use final palabra clave final para una variable si está convirtiendo esa variable en immutable

Al declarar la variable como definitiva, ayuda a los desarrolladores a descartar posibles problemas de modificación de variables en entornos de múltiples subprocesos.

Con la versión java 8, tenemos un concepto más llamado ” effectively final variable “. Una variable no final puede aparecer como variable final.

las variables locales a las que se hace referencia desde una expresión lambda deben ser finales o efectivamente finales

Una variable se considera efectiva final si no se modifica después de la inicialización en el bloque local. Esto significa que ahora puede usar la variable local sin la palabra clave final dentro de una clase anónima o expresión lambda, siempre que sea efectivamente definitiva.

Hasta Java 7, no puede usar una variable local no final dentro de una clase anónima, pero desde Java 8 puede

Eche un vistazo a este artículo

Una respuesta muy simple tenemos 3 casos con Final con variables, Final con métodos && Final con clases …

1.Final con variable: no puedes asignar esta variable más de una vez.

2.Final con métodos: no puedes anular este método …

3.Final con las clases: no se puede extender ninguna clase final

En primer lugar, la palabra clave final se usa para hacer que una variable sea constante. Constante significa que no cambia. Por ejemplo:

 final int CM_PER_INCH = 2.54; 

Declararía la variable final porque un centímetro por pulgada no cambia.

Si intenta anular un valor final, la variable es lo que se declaró primero. Por ejemplo:

 final String helloworld = "Hello World"; helloworld = "A String"; //helloworld still equals "Hello World" 

Hay un error de comstackción que es algo así como:

 local variable is accessed from inner class, must be declared final 

Si su variable no puede declararse definitiva o si no desea declararla definitiva, intente esto:

 final String[] helloworld = new String[1]; helloworld[0] = "Hello World!"; System.out.println(helloworld[0]); helloworld[0] = "A String"; System.out.println(helloworld[0]); 

Esto se imprimirá:

 Hello World! A String