Hacer que los argumentos del método Java sean finales

92

Qué diferencia hay finalentre el siguiente código. ¿Hay alguna ventaja en declarar los argumentos como final.

public String changeTimezone( Timestamp stamp, Timezone fTz, Timezone toTz){  
    return ....
}

public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
        final Timezone toTz){
    return ....
}
Juan
fuente
4
Hay analizadores de código que advierten si un parámetro se reutiliza o reasigna. (Lo mismo para las variables locales) En mi humilde opinión, esta es una mejor manera de capturar dichos parámetros si encuentra que cambiarlos no es deseable.
Peter Lawrey
Creo que Java debería hacer que todos los argumentos del método de entrada sean finales de forma predeterminada. Y luego, si quiero modificar la referencia, tendría que hacerlo manualmente. De esa manera, el factor de culpa evitaría muchos de esos casos.
Sid

Respuestas:

131

Como un parámetro de método formal es una variable local, puede acceder a ellos desde clases anónimas internas solo si están declaradas como finales.

Esto le evita declarar otra variable final local en el cuerpo del método:

 void m(final int param) {
        new Thread(new Runnable() {
            public void run() {
                System.err.println(param);
            }
        }).start();
    }
PeterMmm
fuente
27
+1: este es un caso de uso importante y el único momento en el que lo necesita . (El resto del tiempo es solo una cuestión de lo que es conveniente para ayudar al programador.)
Donal Fellows
3
¿Puedo preguntar el razonamiento detrás de esto?
KodeWarrior
21
Ya no es necesario con Java 8.
Amit Parashar
3
@simgineer lea aquí: stackoverflow.com/questions/28408109/…
PeterMmm
3
@AmitParashar Verdadero, pero Java 8 simplemente te ahorra tener que usar la palabra clave "final" cada vez que tienes que usar la variable en una clase interna ... La realidad es que el compilador simplemente está haciendo implícita la finalidad , todavía necesita que la variable sea efectivamente final ... Por lo tanto, aún obtiene un error de tiempo de compilación en caso de que intente asignarla más tarde. Java 8 : SNEAK 100 :)
varun
38

Extracto de La última palabra sobre la palabra clave final

Parámetros finales

La siguiente muestra declara los parámetros finales:

public void doSomething(final int i, final int j)
{
  // cannot change the value of i or j here...
  // any change would be visible only inside the method...
}

final se utiliza aquí para garantizar que los dos índices i y j no se restablezcan accidentalmente con el método. Es una forma práctica de protegerse contra un error insidioso que cambia erróneamente el valor de sus parámetros. En términos generales, los métodos cortos son una mejor manera de protegerse de esta clase de errores, pero los parámetros finales pueden ser una adición útil a su estilo de codificación.

Tenga en cuenta que los parámetros finales no se consideran parte de la firma del método y el compilador los ignora al resolver las llamadas al método. Los parámetros se pueden declarar finales (o no) sin influencia sobre cómo se anula el método.

granadas
fuente
18
Podría ser mejor usar objetos en lugar de primitivas para este ejemplo, ya que los cambios primitivos siempre solo serán visibles dentro del método. Y en el caso de los objetos, aún puede cambiarlos. Simplemente no puede apuntar a un objeto nuevo. De hecho, ahora que lo pienso, final no cambia nada en comparación con dejarlo fuera, aparte de guardar una declaración de variable con AIC y hacer que el compilador señale modificaciones accidentales de parámetros que no desea modificar por alguna razón. .
Rob Grant
27

La final le impide asignar un nuevo valor a la variable, y esto puede ser útil para detectar errores tipográficos. Estilísticamente, es posible que desee mantener los parámetros recibidos sin cambios y asignarlos solo a variables locales, por lo que final ayudaría a hacer cumplir ese estilo.

Debo admitir que rara vez recuerdo usar final para los parámetros, tal vez debería.

public int example(final int basicRate){
    int discountRate;

    discountRate = basicRate - 10;
    // ... lots of code here 
    if ( isGoldCustomer ) {
        basicRate--;  // typo, we intended to say discountRate--, final catches this
    }
    // ... more code here

    return discountRate;
}
djna
fuente
1
Gran ejemplo de cómo puede ser útil declarar argumentos como finales. Soy parcial a esto, pero también son un bocado para más de 3 parámetros.
JoseHdez_2
14

No hace mucha diferencia. Simplemente significa que no puedes escribir:

stamp = null;
fTz = new ...;

pero aún puedes escribir:

stamp.setXXX(...);
fTz.setXXX(...);

Es principalmente una pista para el programador de mantenimiento que le sigue de que no va a asignar un nuevo valor al parámetro en algún lugar en el medio de su método donde no es obvio y, por lo tanto, podría causar confusión.

Adrian Pronk
fuente
3

La palabra clave final cuando se usa para parámetros / variables en Java marca la referencia como final. En caso de pasar un objeto a otro método, el sistema crea una copia de la variable de referencia y la pasa al método. Al marcar las nuevas referencias como definitivas, las protege de la reasignación. A veces se considera una buena práctica de codificación.

Sid
fuente
1
Tengo que añadir algo: si los parámetros son primitivos, no veo diferencias. Además, si los parámetros son Colecciones (una lista de objetos ...), agregar final no podría evitar que se modifiquen.
Sam003
1
La inmutabilidad es siempre un rasgo deseable. Java no lo tiene listo para usar. Hacer que las variables sean finales al menos asegura la integridad de la referencia.
Sid
1
Estoy de acuerdo. Pero si realmente queremos lograr la inmutabilidad de los objetos, podríamos intentar hacer un clon profundo.
Sam003
2

Para el cuerpo de este método, la finalpalabra clave evitará que las referencias de los argumentos se reasignen accidentalmente dando un error de compilación en esos casos (la mayoría de los IDE se quejarán de inmediato). Algunos pueden argumentar que el uso finalen general siempre que sea posible acelerará las cosas, pero ese no es el caso en las JVM recientes.

dimitrisli
fuente
2

Se enumeran dos ventajas que veo:

1 Marcar el argumento del método como final evita la reasignación del argumento dentro del método

De tu ejemplo

    public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
            final Timezone toTz){
    
    // THIS WILL CAUSE COMPILATION ERROR as fTz is marked as final argument

      fTz = Calendar.getInstance().getTimeZone();     
      return ..
    
    }

En un método complicado, marcar los argumentos como finales ayudará en la interpretación accidental de estos argumentos como variables locales de métodos y reasignarlos como compilador marcará estos casos como se muestra en el ejemplo.

2 Pasando el argumento a una clase interna anónima

Como un parámetro de método formal es una variable local, puede acceder a ellos desde clases anónimas internas solo si están declaradas como finales.

Santhosh Urumese
fuente
1

- En el pasado (antes de Java 8 :-))

Explique el uso de la accesibilidad afectada por la palabra clave "final" de la variable de método para clases internas anónimas.

- En lenguaje de lenguaje moderno (Java 8+) no hay necesidad de tal uso:

Java introdujo variables "efectivamente finales". Las variables locales y los parámetros del método se asumen como finales si el código no implica un cambio de valor de la variable. Entonces, si ve dicha palabra clave en Java8 +, puede asumir que no es necesaria. La introducción de "efectivamente final" nos hace escribir menos código cuando usamos lambdas.

Witold Kaczurba
fuente
0

Es solo una construcción en Java para ayudarlo a definir un contrato y cumplirlo. Una discusión similar aquí: http://c2.com/cgi/wiki?JavaFinalConsideredEvil

Por cierto, (como dice el twiki), marcar args como final es generalmente redundante si está siguiendo buenos principios de programación y ya ha reasignado / redefinido la referencia de argumento entrante.

En el peor de los casos, si redefine la referencia de args, no afectará el valor real pasado a la función, ya que solo se pasó una referencia.

madhurtanwani
fuente
0

Estoy hablando de marcar variables y campos como finales en general, no solo se aplica a los argumentos del método. (Marcar métodos / clases finales es algo completamente diferente).

Es un favor para los lectores / futuros mantenedores de su código. Junto con un nombre sensato de la variable, es útil y tranquilizador para el lector de su código ver / comprender lo que representan las variables en cuestión, y es tranquilizador para el lector que siempre que vea la variable en el mismo ámbito, el significado permanece lo mismo, por lo que no tiene que rascarse la cabeza para descubrir siempre qué significa una variable en cada contexto. Hemos visto demasiados abusos de "reutilización" de variables, lo que hace que incluso un pequeño fragmento de código sea difícil de entender.

RAYO
fuente
-3

La palabra clave final le impide asignar un nuevo valor al parámetro. Me gustaría explicar esto con un ejemplo simple.

Supongamos que tenemos un método

Método 1(){

Date dateOfBirth = nueva fecha ("1/1/2009");

método2 (fecha de nacimiento);

method3 (dateOfBirth); }

public mehod2 (Date dateOfBirth) {
....
....
....
}

public mehod2 (Date dateOfBirth) {
....
....
....
}

En el caso anterior, si se asigna un nuevo valor a "dateOfBirth" en el método2, esto daría como resultado una salida incorrecta del método3. Como el valor que se pasa al método3 no es el que era antes de pasar al método2. Entonces, para evitar esta palabra clave final, se usa para parámetros.

Y esta es también una de las mejores prácticas de codificación de Java.

Kamal
fuente
5
Eso no es del todo correcto. Incluso si el argumento dateOfBirth se cambia a otro valor en method2 (), esto no tendría ningún efecto en method2 (), ya que Java pasa por valor y no por referencia.
Flo