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

StackOverflow https://stackoverflow.com/questions/154314

  •  03-07-2019
  •  | 
  •  

Pregunta

He encontrado un par de referencias ( por ejemplo ) que sugieren utilizar 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 un sentido obvio.

Por un lado, el compilador puede hacer algunas optimizaciones y aclara la intención del programador. Por otro lado, agrega verbosidad y las optimizaciones pueden ser triviales.

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

¿Fue útil?

Solución

Obsesiona más:

  • Campos finales: marcar los campos como finales los obliga a establecerse al final de la construcción, lo que hace que la referencia del 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 en muchos de los casos en los que solía usar campos finales estáticos.

Considera, pero usa con cuidado:

  • 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étodos de plantilla como locos y marcando cosas finales, probablemente estés confiando demasiado en la herencia y no lo suficiente en la delegación.

Ignora a menos que sientas anal:

  • Parámetros del método y variables locales: RARAMENTE hago esto en gran parte porque soy perezoso y encuentro que el código está desordenado. Admito completamente que los parámetros de marcado y las variables locales que no voy a modificar son " righter " ;. Ojalá fuera por defecto. Pero no lo es y encuentro el código más difícil de entender con las finales por todas partes. Si estoy en el código de otra persona, no los sacaré, pero si estoy escribiendo un código nuevo, no los ingresaré. Una excepción es el caso en el que tiene que marcar algo final para poder acceder. desde dentro de una clase interna anónima.

Otros consejos

  

¿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 en tiempo de desarrollo de " final " son al menos tan importantes como los beneficios en tiempo de ejecución. Le dice a los futuros editores del código algo sobre sus 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 gracia. Si los lectores pueden realizar cambios en la clase y desea eliminar el " final " Modificador, pueden hacerlo bajo su propio riesgo. Depende de ellos asegurarse de que la clase maneje bien la extensión.

Marcar una variable " final " (y asignarlo en el constructor) es útil con la inyección de dependencia. Indica el " colaborador " Naturaleza 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 y los locales del método de marcado 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 final generosamente, vea lo que " no puede asignar a la variable final " los errores que genera el compilador (o su IDE), y es posible que descubra por qué la variable llamada " datos " termina en nulo a pesar de que varios comentarios (fuera de fecha) confían en que eso no puede suceder.

Luego, puede corregir algunos de los errores reemplazando las variables reutilizadas con nuevas variables declaradas más cerca del punto de uso. Luego descubre que puede ajustar partes enteras del método con llaves de alcance y, de repente, tiene que presionar una tecla IDE desde " Método de extracción " y tu monstruo se volvió más comprensible.

Si su método no ya es un accidente irreparable, creo que podría ser útil hacer que las cosas sean definitivas para desalentar a las personas a convertirlo en dicho accidente; pero si es un método corto (ver: no se puede mantener) entonces corre el riesgo de agregar mucha verbosidad. En particular, las firmas de la función Java son lo suficientemente difíciles para caber en 80 caracteres, ya que es sin agregar seis más por argumento.

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

Por lo tanto, debe tratar de usar siempre (IMHO) las variables finales cuando se usan condiciones.

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

Y como notó @Recurse (pero aparentemente -1 me) puedes hacer lo anterior sin hacer String name final para obtener el error del compilador (que nunca dije no pudo) pero fácilmente podría hacer que el error del compilador desapareciera configurando el nombre después de la instrucción de cambio que arroja la expresión semántica o, peor aún, olvidarse de break que no puede causar un error (a pesar de lo que @Recurse dice) 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 configuración de 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 única evaluación de qué nombre debe ser. De manera similar a como una función que tiene un valor de retorno siempre debe devolver un valor (ignorando las excepciones), el bloque de cambio de nombre tendrá que resolver el nombre y, por lo tanto, estará vinculado a ese bloque de interruptor, lo que facilita la refactorización de fragmentos de código (es decir, el método de extracción de Eclipe refactor) .

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 función, es decir, una expresión. Observe cómo se ve nuestra declaración de cambio.

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

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

Bueno, todo depende de tu estilo ... si TE GUSTA ver la final cuando no modificarás la variable, entonces úsala. Si NO TE GUSTA verlo ... entonces déjalo fuera.

Personalmente, me gusta la menor 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, así que probablemente no sea una sorpresa que me guste evitar la verbosidad.

Por lo tanto, diría que solo debes elegir la dirección hacia la que te estás inclinando y simplemente seguir con ella (en cualquier caso, intenta ser coherente).


Como nota al margen, he trabajado en proyectos que usan y no usan ese patrón, y no he visto ninguna diferencia en la cantidad de errores o errores ... No creo que sea un patrón eso mejorará enormemente tu cuenta de errores o cualquier otra cosa, pero nuevamente es estilo, y si te gusta expresar la intención de no modificarlo, sigue adelante y úsalo.

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 hrs. En un método horrible (con cientos de líneas de código y múltiples fors, ifs anidadas y todo tipo de malas prácticas) te recomendaría que lo hagas.

 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. :(

Si está escribiendo una aplicación para que alguien tenga que leer el código después de, por ejemplo, 1 año, entonces sí, use final en la variable que no debe modificarse todo el tiempo. Al hacer esto, su código será más " autodocumentado " y también reduce la posibilidad de que otros desarrolladores hagan cosas tontas, como usar una constante local como variable temporal local.

Si estás escribiendo algún código desechable, entonces, no, no te molestes en identificar todas las constantes y hacerlas definitivas.

Usaré final tanto como pueda. Si lo hace, se marcará si accidentalmente cambia el campo. También puse los parámetros del método a final. Al hacerlo, detecté varios errores del código que tomé cuando intentaron "establecer" un parámetro olvidando el paso de Java por valor.

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

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

Para las variables de instancia, las haría definitivas si fueran constantes lógicas.

La variable final tiene muchos usos. Aquí hay sólo unos pocos

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 puede usarse luego para otras partes de sus códigos, o puede ser accedido 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, usted construye una variable final con alcance que agrega un prefijo al parámetro environmentKey. En este caso, la variable final es final solo dentro del alcance 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 alcance de la ejecución del método. Esto le permite arreglar una variable en un método durante la duración del método. ver más 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   
}

Esto es especialmente útil cuando tienes líneas de códigos realmente largas, y generará un error de compilación, por lo que no tendrás un error lógico / de negocios cuando alguien cambie accidentalmente variables que no deberían cambiarse.

Colecciones finales

Caso diferente cuando hablamos de colecciones, debe configurarlas 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

Clases finales y Métodos finales no pueden extenderse ni sobrescribirse respectivamente.

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

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

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

La segunda forma de hacer una final de clase 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 como final te ahorra el problema si descubres que es una final real, para demostrar el aspecto de esta clase de prueba. Parece 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 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, debes marcarlo como final cuando hagas una final de clase implícitamente haciendo que su constructor sea privado.

Como mencionaste, pero prefiero el uso explícito de algo sobre el uso implícito. Esto ayudará a eliminar cierta ambigüedad para los futuros mantenedores de código, incluso si solo es usted.

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.

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

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

Con el lanzamiento de Java 8, tenemos un concepto más llamado " efectivamente variable final " ;. Una variable no final puede acumularse 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 una palabra clave final dentro de una clase anónima o una expresión lambda, siempre que sean efectivamente finales.

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

Eche un vistazo a este artículo

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

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

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

3.Final con clases: no puedes extender ninguna clase final

En primer lugar, la palabra clave final se utiliza para hacer que una variable sea constante. 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 puede ser declarada definitiva o si no quiere declararla final, 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top