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


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

Por un lado, el compilador puede hacer algunas optimizaciones y hace más clara la intención del programador. Por otro lado, añade verbosidad y las optimizaciones pueden ser triviales.

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

 151
Author: nawfal, 2008-09-30

16 answers

Obsesionarse con:

  • Campos finales - Marcar los campos como finales obliga a establecerlos al final de la construcción, haciendo 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 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 finales estáticos.

Considere pero use juiciosamente:

  • Clases finales: el diseño de Framework/API es el único caso en el que lo considero.
  • Métodos finales - Básicamente lo mismo que las clases finales. Si estás usando patrones de método de plantilla como loco y marcando cosas finales, probablemente estés 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 local variables-RARA vez hago esto en gran parte porque soy perezoso y me parece que desordena el código. Admitiré plenamente que marcar parámetros y variables locales que no voy a modificar es "correcto". Ojalá fuera por defecto. Pero no lo es y encuentro el código más difícil de entender con finales por todas partes. Si estoy en el código de otra persona, no voy a sacarlos, pero si estoy escribiendo un código nuevo, no los pondré. Una excepción es el caso en el que tienes que marcar algo final para que puedas accede desde una clase interna anónima.
 154
Author: Alex Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-30 19:14:50

¿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 para usted. Entonces obtienes los beneficios por menos esfuerzo.

 43
Author: Peter Hilton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-30 18:31:04

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

Marcar una clase como "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 gracia. Si los lectores pueden hacer cambios en la clase, y quieren eliminar el modificador "final", pueden hacerlo bajo su propio riesgo. Depende de ellos asegurarse de que la clase manejará extensión bien.

Marcar una variable "final" (y asignarla en el constructor) es útil con la inyección de dependencias. Indica la naturaleza "colaboradora" de la variable.

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

 12
Author: Eric R. Rath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-30 19:06:11

He encontrado que marcar parámetros del método y locales como final es útil como una ayuda de refactorización cuando el método en cuestión es un desastre incomprensible de varias páginas. Sprinkle final liberalmente, vea qué errores de "no se puede asignar a la variable final" arroja el compilador (o su IDE), y puede descubrir por qué la variable llamada "data" termina nula a pesar de que varios comentarios (desactualizados) juran que no puede suceder.

Entonces puede corregir algunos de los errores reemplazando el reutilizado variables con nuevas variables declaradas más cercanas al punto de uso. Luego encuentras que puedes envolver partes enteras del método en llaves de alcance, y de repente estás a una tecla IDE de distancia del "Método de extracción" y tu monstruo se vuelve más comprensible.

Si su método es no ya un naufragio inalcanzable, supongo que podría tener valor hacer que las cosas sean definitivas para desalentar a la gente de convertirlo en dicho naufragio; pero si es un método corto (ver: no inalcanzable), entonces corre el riesgo de añadiendo mucho detalle. En particular, las firmas de funciones Java son lo suficientemente difíciles como para caber en 80 caracteres, ya que es sin agregar seis más por argumento!

 7
Author: Sam Stokes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 01:41:39

Bueno, todo esto depende de tu estilo... si LE GUSTA ver el final cuando no va a modificar la variable, entonces úselo. Si no TE GUSTA verlo... entonces déjalo.

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

Sin embargo, prefiero los lenguajes dinámicos, por lo que probablemente no es una sorpresa que me guste evitar la verbosidad.

Por lo tanto, yo diría que simplemente elija la dirección hacia la que se está inclinando y solo tienes que ir con él (en cualquier caso, tratar de ser consistente).


Como nota al margen, he trabajado en proyectos que usan y no usan tal patrón, y no he visto ninguna diferencia en la cantidad de bugs o errores... No creo que sea un patrón que mejore enormemente su conteo de errores ni nada, pero nuevamente es estilo, y si le gusta expresar la intención de que no lo modifique, adelante y úselo.

 6
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-30 18:31:56

Uso final todo el tiempo para hacer que Java esté más basado en expresiones. Vea las condiciones de Java (if,else,switch) no están basadas en expresiones que siempre he odiado especialmente si está acostumbrado a la programación funcional (es decir, ML, Scala o Lisp).

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

Permítanme darles un ejemplo:{[17]]}

    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 instrucción case y no establece name el compilador fallará. El compilador también fallará si lo hace no romper en todos los casos (que se establece la variable). Esto le permite hacer que Java sea muy similar a las expresiones let de Lisp y hace que su código no tenga sangría masiva (debido a las variables de alcance léxico).

Y como @Recurse señaló (pero aparentemente -1 me) puede hacer lo anterior sin hacer String name final para obtener el error del compilador (que nunca dije que no podría), pero podría fácilmente hacer que el error del compilador desaparezca el nombre de configuración después de la instrucción switch que se deshace la expresión semántica o peor olvidándose de break que no puede causar un error (a pesar de lo que dice @Recurse) sin usar 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 olvidarme de break que también es 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 sola evaluación de qué nombre debe ser. De forma similar a como una función que tiene un valor devuelto siempre debe devolver un valor (ignorando excepciones) el bloque de interruptor de nombre tendrá para resolver nombre y por lo tanto vinculado a ese bloque de interruptor que hace refactorizar trozos de código más fácil (es decir, Eclipe refactor: extract method).

Lo anterior en OCaml:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

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

El match ... with ... se evalúa como una expresión ie de función. Observe cómo se ve nuestra declaración switch.

Aquí hay un ejemplo en Scheme (Raqueta o Pollo):

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))
 6
Author: Adam Gent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-12 04:27:18

Si está escribiendo una aplicación que alguien tendrá que leer el código después de, digamos, 1 año, entonces sí, use final en variable que no debe modificarse todo el tiempo. Al hacer esto, su código se "auto-documentará" 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 algún código desechable, entonces, nah, no te molestes en identificar todas las constantes y hacerlas definitivas.

 4
Author: Alvin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-30 19:17:52

Es útil en parámetros para evitar cambiar el valor del parámetro por accidente e introducir un error sutil. Uso 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, ifs anidados y todo tipo de malas prácticas) Te recomendaría hacerlo.

 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 que en un mundo perfecto esto no sucedería, pero.. bien.. a veces tienes que apoyar el código de otros. :(

 4
Author: OscarRyz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 01:55:52

Usaré final tanto como pueda. Al hacerlo, se marcará si cambia el campo sin querer. También establezco los parámetros del método en final. Al hacerlo, he cogido varios errores de código que he tomado cuando intentan 'establecer' un parámetro olvidando Java pasa por valor.

 3
Author: Javamann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-30 19:22:02

No está claro si esto es obvio, pero hacer que un parámetro de método sea definitivo afecta solo al cuerpo del método. NO transmite al invocador ninguna información interesante sobre las intenciones del método. El objeto que se pasa todavía puede ser mutado dentro del método (las finales no son conss), y el alcance de la variable está dentro del método.

Para responder a su pregunta precisa, no me molestaría en hacer una instancia o variable local (incluyendo parámetros del método) final a menos que el código lo requiera (por ejemplo, la variable es referenciada desde una clase interna), o para aclarar alguna lógica realmente complicada.

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

 2
Author: ykaganovich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-01 01:09:09

Hay muchos usos para la variable final. Estos son 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 entonces para otras partes de sus códigos, o acceder por otras clases, de esa manera si alguna vez cambiara 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, se construye una variable final de ámbito que agrega un prefijo al parámetro environmentKey. En este caso, el final la variable es final solo dentro del ámbito de ejecución, que es diferente en cada ejecución del método. Cada vez que se introduce el método, se reconstruye el final. Tan pronto como se construye, no se puede cambiar durante el alcance de la ejecución del método. Esto le permite fijar una variable en un método durante la duración del método. véase a continuación:

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 usted tiene realmente largas líneas de códigos, y lo hará generar error del compilador para que no se ejecute en logic / business error cuando alguien accidentalmente cambia variables que no deben cambiarse.

Colecciones finales

Caso Diferente cuando estamos hablando de Colecciones, es necesario establecer como inmodificable.

 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 establece 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 pueden ampliarse ni sobrescribirse respectivamente.

EDITAR: PARA ABORDAR EL PROBLEMA DE CLASE FINAL CON RESPECTO A LA ENCAPSULACIÓN:

Hay dos maneras de hacer que una clase sea final. La primera es usar la palabra clave final en la declaración de clase:

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

La segunda forma de hacer una clase final es declarar a 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 el problema si descubre que es realmente un final, para demostrar mirar esta clase de prueba. parece público al principio vistazo.

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 razón para que la clase sea final. La clase Test es un buen ejemplo de cómo las clases finales implícitas pueden causar problemas.

Así que usted debe marcar final cuando implícitamente hacer una clase final por lo que es constructor privado.

 2
Author: mel3kings,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-21 06:40:29

Algo así como una compensación, pero prefiero el uso explícito de algo sobre el uso implícito. Esto ayudará a eliminar cierta ambigüedad para futuros mantenedores de código, incluso si es solo usted.

 1
Author: Sean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-30 18:38:05

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

Aparte de eso, lo que has dicho es correcto.

 1
Author: anjanb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-10 18:45:42

Use la palabra clave final para una variable si está creando esa variable como immutable

Al declarar la variable como final, ayuda a los desarrolladores a descartar posibles problemas de modificación de variables en entornos altamente multihilo.

Con la versión de java 8, tenemos un concepto más llamado "effectively final variable". Una variable no final puede elevarse como variable final.

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

Se considera una variablefinal efectivo 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 sean efectivamente finales.

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

Echa un vistazo a esto artículo

 0
Author: Ravindra babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-28 13:33:25

Una respuesta muy simple tenemos 3 casos con Final con variables ,Final con métodos y Final con clases..

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

2.Final con métodos: u no puede anular este método..

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

 0
Author: Mohamed Ayed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-19 03:15:28

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

final int CM_PER_INCH = 2.54;

Usted 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 compilación que es algo así como:

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

Si su variable no se puede declarar definitiva o si no desea declararla prueba 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
 -1
Author: Jared Burrows,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-25 16:05:10