2010-08-24 10 views
20

He estado usando PMD para ayudar a detectar problemas potenciales en mi código de Java, y he estado encontrando su consejo para dividirlo entre el útil, el idiosincrásico y el "¡WTF!".Todo es final

Una de las cosas que siempre me dice que haga es usar la palabra clave final para literalmente cada variable a la que puedo adjuntar, incluidos los parámetros de entrada. Para constantes reales esto parece sensato, pero para otras cosas me parece extraño, posiblemente incluso un poco contraproducente.

¿Hay ventajas/desventajas concretas de colgar final en cada declaración de variable que pueda?

+1

Ver http://stackoverflow.com/questions/137868/using-final-modifier-whenever-applicable-in-java –

Respuesta

21

"Cada declaración de variable que pueda" suena un poco extrema, pero final es realmente beneficioso de muchas maneras. A veces deseo que final fuera el comportamiento predeterminado, y no requiriera ninguna palabra clave, pero las verdaderas "variables" requerían un modificador variable. Scala adoptó algo así como este enfoque con sus val y var palabras clave — usando val (se recomienda encarecidamente la palabra clave similar a final).

Es especialmente importante considerar cuidadosamente si cada variable miembro es final, volatile, o ninguna, porque la seguridad de la clase depende de hacer esto bien. Los valores asignados a las variables final y volatile siempre son visibles para otros hilos, sin utilizar un bloque synchronized.

Para las variables locales, no es tan crítico, pero usar final puede ayudarlo a razonar sobre su código más claramente y evitar algunos errores. Si no espera un valor para cambiar dentro de un método, dígalo con final, y deje que el compilador encuentre violaciones inadvertidas de esta expectativa.No conozco ninguno que lo haga actualmente, pero es fácilmente concebible que un compilador JIT pueda usar esta sugerencia para mejorar el rendimiento también.

En la práctica, no declaro las variables locales final siempre que puedo. No me gusta el desorden visual y parece engorroso. Pero eso no significa que no sea algo que debería hacer.

Se ha realizado una propuesta para agregar la palabra clave var a Java con el objetivo de admitir la inferencia de tipo. Pero como parte de esa propuesta, ha habido una serie de sugerencias para formas adicionales de especificar la inmutabilidad de la variable local. Por ejemplo, una sugerencia fue agregar también la palabra clave val para declarar una variable inmutable con tipo inferido. Alternativamente, algunos recomiendan usar final y var juntos.

+0

Visto en retrospectiva, eso habría sido el mejor. Sin embargo, habría quebrado la semántica C, que se buscó explícitamente para dar a los programadores C++ una transición fácil. –

2

Esta es una expresión común para herramientas como PMD. Por ejemplo, a continuación se muestran las reglas correspondientes en Checkstyle. Es realmente una cuestión de estilo/preferencia y se podría discutir por ambas partes.

En mi opinión, usar el final para los parámetros del método y las variables locales (cuando corresponda) es un buen estilo. La expresión "diseño para extensión" es discutible.

5

último le dice al lector que el valor o referencia asignada primero es el mismo en cualquier momento posterior.

Como todo lo que puede ser definitivo es definitivo en este escenario, una falta último le dice al lector que el valor se cambio posterior, y para tenerlo en cuenta.

+0

Eso tiene sentido para primitivos, o para objetos inmutables como String. Pero, ¿eso fomenta una falsa sensación de seguridad para los objetos mutables? – BlairHippo

+0

Una final faltante solo te dice que puede cambiar más tarde, sin duda no será así. Asumir eso es simplemente incorrecto. Si una clase no se define como final, se puede subclasificar, no quiere decir que sí. El uso de final para todas las variables está abierto al debate (desde aquí) por lo que su ausencia se puede atribuir fácilmente a la preferencia del codificador. – Robin

+1

@Robin, tenga en cuenta que el OP dijo "Todo es final". Ese es el caso que estoy discutiendo. Por favor, lea las preguntas correctamente antes de downvoting las respuestas. –

1

PMD también tiene reglas de opción que puede activar que se quejan acerca de final; es una regla arbitraria.

Si estoy haciendo un proyecto en el que la API se está exportando a otro equipo, o al mundo, deje la regla PMD tal como está. Si solo está desarrollando algo que siempre y para siempre será una API cerrada, desactive la regla y ahórrese un poco de tiempo.

1

Estas son algunas razones por las que puede ser beneficiosa a haber casi todo etiquetado comofinal

final Constantes

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 ser usado luego para otras partes de sus códigos, o accedido por otras clases, de esa manera si alguna vez cambiaras el valor, no tendrías 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 última variable con ámbito que añade un prefijo al environmentKey parámetro. 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 corregir una variable en un método por 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 
} 

Estos son especialmente útiles cuando se tiene muy largas líneas de código, y se generarán error del compilador de modo que no se quede en la lógica/error de negocio cuando alguien accidentalmente cambia variables que no deberían cambiarse.

Colecciones finales

Diferentes casos cuando estamos hablando acerca de las colecciones, que tiene necesidad de hacerlo como un 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 se establece como inmodificable:

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

clases finales y métodos finales no puede ser extendida o sobrescrito, respectivamente.

EDIT: Para abordar el problema de la clase final con respecto a ENCAPSULACIÓN:

Hay dos maneras de hacer una última clase. La primera es utilizar la última palabra clave en la declaración de clase:

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

La segunda manera de hacer una clase final es declarar la totalidad de sus constructores como privada:

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

Marcándola final que ahorra el problema 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 . . . 
    } 
} 

Por desgracia, ya que el único constructor de la clase es privada, 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.