No puedo entender dónde la final
palabra clave es realmente útil cuando se usa en los parámetros del método.
Si excluimos el uso de clases anónimas, legibilidad y declaración de intenciones, me parece casi inútil.
Hacer cumplir que algunos datos permanezcan constantes no es tan fuerte como parece.
Si el parámetro es primitivo, no tendrá efecto ya que el parámetro se pasa al método como un valor y cambiarlo no tendrá ningún efecto fuera del alcance.
Si estamos pasando un parámetro por referencia, entonces la referencia en sí es una variable local y si la referencia se cambia desde dentro del método, eso no tendría ningún efecto desde fuera del alcance del método.
Considere el ejemplo de prueba simple a continuación. Esta prueba pasa aunque el método cambió el valor de la referencia que se le dio, no tiene ningún efecto.
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
fuente
int foo(int* bar)
y el últimoint foo(int* &bar)
. El último pasa un puntero por referencia, el primero pasa una referencia por valor.Respuestas:
Detener la reasignación de una variable
Si bien estas respuestas son intelectualmente interesantes, no he leído la respuesta simple y breve:
Si la variable es una variable estática, una variable miembro, una variable local o una variable de argumento / parámetro, el efecto es completamente el mismo.
Ejemplo
Veamos el efecto en acción.
Considere este método simple, donde las dos variables ( arg y x ) pueden ser reasignadas a diferentes objetos.
Marque la variable local como final . Esto da como resultado un error del compilador.
En su lugar, marquemos la variable del parámetro como final . Esto también resulta en un error del compilador.
Moraleja de la historia:
Nunca reasigne argumentos
Como buena práctica de programación (en cualquier lenguaje), nunca debe reasignar una variable de parámetro / argumento a un objeto que no sea el objeto pasado por el método de llamada. En los ejemplos anteriores, nunca se debe escribir la línea
arg =
. Como los humanos cometen errores y los programadores son humanos, solicitemos ayuda al compilador. Marque cada variable de parámetro / argumento como 'final' para que el compilador pueda encontrar y marcar tales reasignaciones.En retrospectiva
Como se señaló en otras respuestas ... Dado el objetivo de diseño original de Java de ayudar a los programadores a evitar errores tontos, como leer más allá del final de una matriz, Java debería haber sido diseñado para aplicar automáticamente todas las variables de parámetro / argumento como 'final'. En otras palabras, los argumentos no deben ser variables . Pero la retrospectiva es una visión 20/20, y los diseñadores de Java tenían las manos ocupadas en ese momento.
Entonces, ¿siempre agregar
final
a todos los argumentos?¿Debemos agregar
final
a todos y cada uno de los parámetros del método que se declara?➥ Agregue
final
solo cuando el código del método es largo o complicado, donde el argumento puede confundirse con una variable local o miembro y posiblemente reasignarse.Si acepta la práctica de no reasignar nunca un argumento, estará inclinado a agregar un
final
a cada uno. Pero esto es tedioso y hace que la declaración sea un poco más difícil de leer.Para el código simple corto donde el argumento es obviamente un argumento, y no una variable local ni una variable miembro, no me molesto en agregar el
final
. Si el código es bastante obvio, sin posibilidad de que yo ni ningún otro programador realice tareas de mantenimiento o refactorice accidentalmente confundiendo la variable de argumento como algo más que un argumento, entonces no se moleste. En mi propio trabajo, agregofinal
solo código más largo o más involucrado donde un argumento puede confundirse con una variable local o miembro.Otro caso agregado para la integridad
fuente
message = ( msg || 'Hello World"' )
. Simplemente no hay razón para no usar una var. El único costo es unos pocos bytes de memoria.message = ( msg || 'Hello World"' )
me arriesga más tarde accidentalmentemsg
. Cuando el contrato que pretendo es "el comportamiento sin argumento / nulo / indefinido es indistinguible de aprobar"Hello World"
", es una buena práctica de programación comprometerse con él al principio de la función. [Esto se puede lograr sin reasignación comenzando con,if (!msg) return myfunc("Hello World");
pero eso se vuelve difícil de manejar con múltiples argumentos.] En los raros casos en que la lógica en la función debería importar si se usó el valor predeterminado, prefiero designar un valor centinela especial (preferiblemente público) .message
ymsg
. Pero si lo llamara algo asíprocessedMsg
o algo más que proporciona un contexto adicional, la posibilidad de un error es mucho menor. Concéntrese en lo que dice, no en "cómo" lo dice. ;)A veces es bueno ser explícito (para facilitar la lectura) que la variable no cambia. Aquí hay un ejemplo simple donde usar
final
puede ahorrar algunos posibles dolores de cabeza:Si olvida la palabra clave 'this' en un setter, entonces la variable que desea establecer no se establece. Sin embargo, si usó la
final
palabra clave en el parámetro, el error se detectaría en el momento de la compilación.fuente
Sí, excluyendo clases anónimas, legibilidad y declaración de intenciones es casi inútil. Sin embargo, ¿son inútiles esas tres cosas?
Personalmente, tiendo a no usar
final
para variables y parámetros locales a menos que esté usando la variable en una clase interna anónima, pero ciertamente puedo ver el punto de aquellos que quieren dejar en claro que el valor del parámetro en sí no cambiará (incluso si el objeto al que hace referencia cambia su contenido). Para aquellos que encuentran que eso aumenta la legibilidad, creo que es algo completamente razonable.Su punto sería más importante si alguien eran en realidad afirmando que se mantenga constante de datos de una manera que no lo hace - pero no puedo recordar haber visto ninguna de dichas reivindicaciones. ¿Estás sugiriendo que hay un cuerpo significativo de desarrolladores que sugiere que
final
tiene más efecto de lo que realmente tiene?EDITAR: Realmente debería haber resumido todo esto con una referencia de Monty Python; la pregunta parece algo similar a preguntar "¿Qué han hecho los romanos por nosotros?"
fuente
Déjame explicarte un poco sobre el único caso en el que tienes que usar el final, que Jon ya mencionó:
Si crea una clase interna anónima en su método y usa una variable local (como un parámetro de método) dentro de esa clase, entonces el compilador lo obliga a hacer que el parámetro sea final:
Aquí los parámetros
from
yto
deben ser finales para que puedan usarse dentro de la clase anónima.La razón de este requisito es esta: las variables locales viven en la pila, por lo tanto, existen solo mientras se ejecuta el método. Sin embargo, la instancia de clase anónima se devuelve del método, por lo que puede vivir mucho más tiempo. No puede preservar la pila, porque es necesaria para las llamadas de método posteriores.
Entonces, lo que Java hace es colocar copias de esas variables locales como variables de instancia ocultas en la clase anónima (puede verlas si examina el código de bytes). Pero si no fueran finales, uno podría esperar que la clase anónima y el método vean los cambios que el otro hace a la variable. Para mantener la ilusión de que solo hay una variable en lugar de dos copias, tiene que ser final.
fuente
final
parámetro de función y un parámetro de función no final a los ojos de HotSpot?Yo uso final todo el tiempo en los parámetros.
¿Agrega eso tanto? Realmente no.
¿Lo apagaría? No.
La razón: encontré 3 errores en los que la gente había escrito código descuidado y no había podido establecer una variable miembro en los accesores. Todos los errores resultaron difíciles de encontrar.
Me gustaría ver que esto sea el predeterminado en una futura versión de Java. El paso por valor / referencia hace tropezar a un montón de programadores junior.
Una cosa más ... mis métodos tienden a tener un número bajo de parámetros, por lo que el texto adicional en una declaración de método no es un problema.
fuente
Usar final en un parámetro de método no tiene nada que ver con lo que sucede con el argumento en el lado del llamante. Solo tiene la intención de marcarlo como que no cambia dentro de ese método. Cuando trato de adoptar un estilo de programación más funcional, veo el valor en eso.
fuente
final
en parámetros en interfaces / declaraciones de métodos abstractos.Personalmente, no uso final en los parámetros del método, porque agrega demasiado desorden a las listas de parámetros. Prefiero hacer cumplir que los parámetros del método no se cambien a través de algo como Checkstyle.
Para las variables locales que uso final siempre que sea posible, incluso dejo que Eclipse lo haga automáticamente en mi configuración para proyectos personales.
Ciertamente me gustaría algo más fuerte como C / C ++ const.
fuente
Como Java pasa copias de los argumentos, siento que la relevancia de esto
final
es bastante limitada. Supongo que el hábito proviene de la era de C ++, donde podría prohibir que se cambie el contenido de referencia haciendo unconst char const *
. Siento que este tipo de cosas te hace creer que el desarrollador es inherentemente estúpido como un maldito y necesita estar protegido contra todos los personajes que escribe. Con toda humildad, puedo decir que escribo muy pocos errores aunque omitofinal
(a menos que no quiera que alguien anule mis métodos y clases). Tal vez solo soy un desarrollador de la vieja escuela.fuente
Nunca uso final en una lista de parámetros, solo agrega desorden como han dicho los encuestados anteriores. También en Eclipse puede configurar la asignación de parámetros para generar un error, por lo que usar final en una lista de parámetros me parece bastante redundante. Curiosamente, cuando habilité la configuración de Eclipse para la asignación de parámetros que generaba un error, captó este código (así es como recuerdo el flujo, no el código real):
Jugando al abogado del diablo, ¿qué tiene de malo hacer esto?
fuente
Respuesta corta:
final
ayuda un poco, pero ... usa la programación defensiva en el lado del cliente.De hecho, el problema
final
es que solo impone que la referencia no se modifique, permitiendo alegremente que los miembros del objeto a los que se hace referencia muten, sin que el llamante lo sepa. Por lo tanto, la mejor práctica a este respecto es la programación defensiva en el lado de la persona que llama, creando instancias profundamente inmutables o copias profundas de objetos que están en peligro de ser asaltados por API sin escrúpulos.fuente
Una razón adicional para agregar declaraciones finales a los parámetros es que ayuda a identificar las variables que necesitan ser renombradas como parte de una refactorización de "Método de extracción". Descubrí que agregar el final a cada parámetro antes de comenzar una refactorización de métodos grandes me dice rápidamente si hay algún problema que deba abordar antes de continuar.
Sin embargo, generalmente los elimino como superfluos al final de la refactorización.
fuente
Seguimiento de lo que postea Michel. Yo mismo hice otro ejemplo para explicarlo. Espero que pueda ayudar.
Del código anterior, en el método thisIsWhy () , en realidad no asignamos el [argumento MyObj obj] a una referencia real en MyParam. En cambio, solo usamos el [argumento MyObj obj] en el método dentro de MyParam.
Pero después de que terminemos el método thisIsWhy () , ¿ debería existir el argumento (objeto) MyObj?
Parece que debería, porque podemos ver en main todavía llamamos al método showObjName () y necesita llegar a obj . MyParam todavía usa / alcanza el argumento del método, ¡incluso el método ya fue devuelto!
Cómo Java realmente logra esto es generar una copia también es una referencia oculta del argumento MyObj obj dentro del objeto MyParam (pero no es un campo formal en MyParam para que no podamos verlo)
Como llamamos "showObjName", usará esa referencia para obtener el valor correspondiente.
Pero si no pusimos el argumento final, lo que lleva a una situación, podemos reasignar una nueva memoria (objeto) al argumento MyObj obj .
¡Técnicamente no hay choque en absoluto! Si se nos permite hacer eso, a continuación será la situación:
Sin choque, pero "¡¡¡CONFUSENTE !!" Porque todos usan el mismo "nombre de referencia" que es "obj" .
Para evitar esto, configúrelo como "final" para evitar que el programador haga el código "propenso a errores".
fuente