En Java, ¿debo usar "final" para parámetros y locales, incluso cuando no tengo que hacerlo?

95

Java permite marcar variables (campos / locales / parámetros) como final , para evitar que se reasignen a ellos. Me resulta muy útil en los campos, ya que me ayuda a ver rápidamente si algunos atributos, o toda una clase, deben ser inmutables.

Por otro lado, me parece mucho menos útil con los locales y los parámetros, y generalmente evito marcarlos como final incluso si nunca se los volverá a asignar (con la obvia excepción cuando deben ser utilizado en una clase interna). Últimamente, sin embargo, he encontrado un código que usaba final siempre que podía, lo que supongo que técnicamente proporciona más información.

Ya no confío en mi estilo de programación, me pregunto cuáles son otras ventajas y desventajas de aplicar final en cualquier lugar, cuál es el estilo más común de la industria y por qué.

    
pregunta Oak 16.02.2011 - 09:23

5 respuestas

61

Uso final de la misma manera que tú. Para mí, parece superfluo en las variables locales y los parámetros del método, y no transmite información adicional útil.

Una cosa importante es que esforzarse por mantener mis métodos cortos y limpios , cada uno haciendo una sola tarea. Por lo tanto, mis variables y parámetros locales tienen un alcance muy limitado, y se usan solo para un solo propósito. Esto minimiza las posibilidades de reasignarlos inadvertidamente.

Además, como seguramente sabrá, final no garantiza que no pueda cambiar el valor / estado de una variable (no primitiva). Solo que no puede reasignar la referencia a ese objeto una vez que se haya inicializado. En otras palabras, funciona a la perfección solo con variables de tipos primitivos o inmutables. Considera

final String s = "forever";
final int i = 1;
final Map<String, Integer> m = new HashMap<String, Integer>();

s = "never"; // compilation error!
i++; // compilation error!
m.put(s, i); // fine

Esto significa que, en muchos casos, todavía no facilita la comprensión de lo que ocurre dentro del código, y esto puede ser un malentendido, lo que puede causar errores sutiles que son difíciles de detectar.

    
respondido por el Péter Török 16.02.2011 - 09:28
56

Tu estilo de programación Java y tus pensamientos están bien, no necesitas dudar de ti mismo allí.

  

Por otro lado, me parece mucho   menos útil con los locales y   Parámetros, y generalmente evito.   marcándolos como finales incluso si   nunca será re-asignado a (con   La excepción obvia cuando necesitan.   para ser utilizado en una clase interna).

Esto es exactamente por lo que debe usar la palabra clave final . Usted declara que USTED sabe que nunca se volverá a asignar, pero nadie más lo sabe. El uso de final inmediatamente desambigua su código un poquito más.

    
respondido por el Jonathan Khoo 16.02.2011 - 09:44
29

Una de las ventajas de usar final / const siempre que sea posible es que reduce la carga mental para el lector de su código.

Puede estar seguro de que el valor / referencia nunca se modificará más adelante. Por lo tanto, no necesita prestar atención a las modificaciones para comprender el cálculo.

He cambiado de opinión con respecto a esto después de aprender lenguajes de programación puramente funcionales. Chico, qué alivio, si puedes confiar en una "variable" para mantener siempre su valor inicial.

    
respondido por el LennyProgrammers 16.02.2011 - 09:36
15

Considero que final en los parámetros del método y las variables locales es ruido de código. Las declaraciones de métodos de Java pueden ser bastante largas (especialmente con los genéricos), ya no es necesario hacerlas más.

Si las pruebas unitarias se escriben correctamente, se asignará a los parámetros que son "dañinos", por lo que nunca debería ser realmente un problema. La claridad visual es más importante que evitar un error posible que no se detecte porque las pruebas de unidad no tienen cobertura suficiente.

Herramientas como FindBugs y CheckStyle que pueden configurarse para dividir la compilación si la asignación se realiza a parámetros o variables locales, si te interesan profundamente esas cosas.

Por supuesto, si necesita para que sean definitivos, por ejemplo, porque está usando el valor en una clase anónima, entonces no hay problema, esa es la solución más simple y limpia.

Además del efecto obvio de agregar palabras clave adicionales a sus parámetros y, por lo tanto, IMHO camuflándolos, agregar parámetros finales al método a menudo puede hacer que el código en el cuerpo del método sea menos legible, lo que empeora el código: "bueno" ", el código debe ser lo más legible y simple posible. Para un ejemplo artificial, digamos que tengo un método que necesita trabajar con mayúsculas y minúsculas.

Sin final :

public void doSomething(String input) {
    input = input.toLowerCase();
    // do a few things with input
}

Simple. Limpiar. Todo el mundo sabe lo que está pasando.

Ahora con 'final', opción 1:

public void doSomething(final String input) {
    final String lowercaseInput = input.toLowerCase();
    // do a few things with lowercaseInput
}

Mientras que hacer los parámetros final impide que el codificador agregue el código más abajo al pensar que está trabajando con el valor original, existe el mismo riesgo de que el código más abajo pueda usar input en lugar de lowercaseInput , lo cual no debería y contra lo que no se puede proteger, porque no puedes sacarlo del alcance (o incluso asignar null a input si eso ayudaría de todos modos).

Con 'final', opción 2:

public void doSomething(final String input) {
    // do a few things with input.toLowerCase()
}

Ahora acabamos de crear aún más ruido de código e introdujimos un golpe de rendimiento al tener que invocar toLowerCase() n veces.

Con 'final', opción 3:

public void doSomething(final String input) {
    doSomethingPrivate(input.toLowerCase());
}

/** @throws IllegalArgumentException if input not all lower case */
private void doSomethingPrivate(final String input) {
    if (!input.equals(input.toLowerCase())) {
        throw new IllegalArgumentException("input not lowercase");
    }
    // do a few things with input
}

Habla sobre el ruido del código. Esto es un choque de trenes. Tenemos un nuevo método, un bloque de excepción requerido, porque otro código puede invocarlo incorrectamente. Más pruebas unitarias para cubrir la excepción. Todo para evitar una línea simple e IMHO preferible e inofensiva.

También existe el problema de que los métodos no deben ser tan largos que no se pueden captar visualmente fácilmente y saber de un vistazo que se ha realizado una asignación a un parámetro.

Creo que es una buena práctica / estilo que si asigna un parámetro lo hace al principio del método, preferiblemente en la primera línea o inmediatamente después de la verificación de entrada básica, reemplazándolo para la todo , que tiene un efecto consistente dentro del método. Los lectores saben que esperan que cualquier asignación sea obvia (cerca de la declaración de la firma) y en un lugar coherente, lo que mitiga en gran medida el problema que la adición final intenta evitar. En realidad, rara vez asigno parámetros, pero si lo hago, siempre lo hago en la parte superior de un método.

Tenga en cuenta también que final en realidad no lo protege como parece a primera vista:

public void foo(final Date date) {
    date.setTime(0); 
    // code that uses date
}

final no lo protege completamente a menos que el tipo de parámetro sea primitivo o inmutable.

    
respondido por el Bohemian 26.08.2016 - 03:28
7

Dejo que eclipse ponga final antes de cada variable local, ya que lo considero para que el programa sea más fácil de leer. No lo hago con parámetros, ya que quiero mantener la lista de parámetros lo más corta posible, idealmente debería caber en una línea.

    
respondido por el maaartinus 16.02.2011 - 10:35

Lea otras preguntas en las etiquetas