Argumentos finales en los métodos de interfaz: ¿cuál es el punto?

190

En Java, es perfectamente legal definir finalargumentos en métodos de interfaz y no obedecer eso en la clase de implementación, por ejemplo:

public interface Foo {
    public void foo(int bar, final int baz);
}

public class FooImpl implements Foo {

    @Override
    public void foo(final int bar, int baz) {
        ...
    }
}

En el ejemplo anterior, bary baztiene las finaldefiniciones opuestas en la clase VS la interfaz.

Del mismo modo, no finalse aplican restricciones cuando un método de clase extiende a otro, ya sea abstracto no.

Si bien finaltiene algún valor práctico dentro del cuerpo del método de clase, ¿hay algún punto que especifique los finalparámetros del método de interfaz?

mindas
fuente
finalde todos modos no hace nada con los tipos nativos, ya que están copiados.
Paul Tomblin
11
Solo como un punto de discusión: acabo de probarlo y si dos interfacedefiniciones varían solo en el finalatributo de un argumento, entonces los .classarchivos resultantes son byte por byte de forma idéntica (y, por supuesto, javap -vproducen el mismo resultado). ¡Lo mismo es cierto para dos clases que solo difieren en finalun atributo, por cierto!
Joachim Sauer
2
@Paul: hace exactamente lo mismo que con los tipos de referencia: evita que los argumentos en sí mismos se modifiquen (si se usan en la implementación).
Joachim Sauer
Tiene tanta relevancia como el público en la firma del método.
Robin
2
@Deepak: te veo pidiendo ejemplos de trabajo para todo tipo de preguntas, incluso cuando no parece tener mucho sentido. Creo que deberías intentar aprender algo abstracto: intenta pensar en un problema sin tener un código ejecutable frente a ti. Le ayudará mucho a largo plazo.
Joachim Sauer

Respuestas:

105

No parece que tenga sentido. De acuerdo con la Especificación del lenguaje Java 4.12.4 :

Declarar una variable final puede servir como documentación útil de que su valor no cambiará y puede ayudar a evitar errores de programación.

Sin embargo, un finalmodificador en un parámetro de método no se menciona en las reglas para hacer coincidir las firmas de los métodos anulados, y no tiene ningún efecto en la persona que llama, solo dentro del cuerpo de una implementación. Además, como señaló Robin en un comentario, el finalmodificador en un parámetro de método no tiene efecto en el código de bytes generado. (Esto no es cierto para otros usos de final).

Ted Hopp
fuente
¿El parámetro del método también califica como variable? Obviamente es en la práctica, pero ¿está en el contexto de la especificación?
Mindas
11
No se puede usar para hacer coincidir las firmas, ya que no aparece en el archivo .class real. Es solo para el compilador.
Robin
@mindas: el JLS dice que hay siete tipos de variables . Los parámetros del método son el cuarto en la lista.
Ted Hopp
3
Sin embargo, la documentación es casi inútil, ya que el finalmodificador no se aplica en la clase implementadora. La firma de su interfaz puede simplemente mentir.
Stefan Haberl
La especificación del lenguaje Java 8 ahora dice que hay ocho tipos de variables (en lugar de siete, agregaron parámetros lambda ). Los parámetros del método siguen siendo el cuarto en la lista (al menos algunas cosas en la vida parecen estables. :-)).
Ted Hopp
25

Algunos IDE copiarán la firma del método abstracto / interfaz al insertar un método de implementación en una subclase.

No creo que haga ninguna diferencia para el compilador.

EDITAR: Si bien creo que esto era cierto en el pasado, no creo que los IDE actuales sigan haciendo esto.

Peter Lawrey
fuente
2
Punto válido, aunque no creo que haya muchos IDEs cuando se implementó esta característica (o se dejó accidentalmente) :-)
mindas
2
Creo que está en la categoría de static transientcampos. ;)
Peter Lawrey
1
Y constructores públicos en una clase abstracta.
Peter Lawrey
1
IntelliJ hace esto. Mi versión es IntelliJ IDEA 2016.1.3 Build # IU-145.1617.
Dormouse
1
@ Dormouse, es interesante, porque Android Studio (3.1.4 Build # AI-173.4907809) no :(
The Godfather
18

Las anotaciones finales de los parámetros del método siempre son relevantes para la implementación del método, nunca para la persona que llama. Por lo tanto, no hay una razón real para usarlos en las firmas de métodos de interfaz. A menos que desee seguir el mismo estándar de codificación consistente, que requiere parámetros de método finales, en todas las firmas de métodos. Entonces es bueno poder hacerlo.

jmg
fuente
6

Actualización: la respuesta original a continuación se escribió sin comprender completamente la pregunta y, por lo tanto, no aborda directamente la pregunta. :)Sin embargo, debe ser informativa para aquellos que buscan comprender el uso general de la finalpalabra clave.

En cuanto a la pregunta, me gustaría citar mi propio comentario a continuación.

Creo que no estás obligado a implementar la finalidad de un argumento para dejarte libre de decidir si debe ser final o no en tu propia implementación.

Pero sí, suena bastante extraño que pueda declararlo finalen la interfaz, pero que no sea definitivo en la implementación. Hubiera tenido más sentido si:

a. finalla palabra clave no estaba permitida para los argumentos del método de interfaz (abstracto) (pero puede usarla en la implementación), o
b. declarar un argumento como finalen la interfaz obligaría a que se declare finalen la implementación (pero no forzado para no finales).


Puedo pensar en dos razones por las que una firma de método puede tener finalparámetros: Frijoles y Objetos (en realidad, ambos son la misma razón, pero contextos ligeramente diferentes ) .

Objetos:

public static void main(String[] args) {
    StringBuilder cookingPot = new StringBuilder("Water ");
    addVegetables(cookingPot);
    addChicken(cookingPot);
    System.out.println(cookingPot.toString());
    // ^--- OUTPUT IS: Water Carrot Broccoli Chicken ChickenBroth 
    //      We forgot to add cauliflower. It went into the wrong pot.
}

private static void addVegetables(StringBuilder cookingPot) {
    cookingPot.append("Carrot ");
    cookingPot.append("Broccoli ");
    cookingPot = new StringBuilder(cookingPot.toString());
    //   ^--- Assignment allowed...
    cookingPot.append("Cauliflower ");
}

private static void addChicken(final StringBuilder cookingPot) {
    cookingPot.append("Chicken ");
    //cookingPot = new StringBuilder(cookingPot.toString());
    //     ^---- COMPILATION ERROR! It is final.
    cookingPot.append("ChickenBroth ");
}

La finalpalabra clave aseguró que no crearemos accidentalmente una nueva olla local al mostrar un error de compilación cuando intentamos hacerlo. Esto aseguró que el caldo de pollo se agregue a nuestra olla original que addChickenobtuvo el método. Compare esto con el lugar addVegetablesdonde perdimos la coliflor porque agregó eso a una nueva olla local en lugar de la olla original que tenía.

Frijoles: es el mismo concepto que los objetos (como se muestra arriba) . Los frijoles son esencialmente Objects en Java. Sin embargo, los beans (JavaBeans) se utilizan en diversas aplicaciones como una forma conveniente de almacenar y pasar una colección definida de datos relacionados. Así como addVegetablespodría estropear el proceso de cocción al crear una nueva olla StringBuildery tirarla con la coliflor, también podría hacer lo mismo con una olla JavaBean .

ADTC
fuente
Okay. Realmente no es una respuesta correcta a la pregunta (ya que no estoy obligado a hacer que mi implementación del método de la interfaz tome argumentos finales, incluso si la interfaz lo dice), pero sigue siendo útil, ya que es una buena explicación de por qué para empezar, hacer una buena idea de los parámetros del método es final.
Phil
Creo que no estás obligado a implementar la finalidad de un argumento para dejarte libre de decidir si debe ser final o no en tu propia implementación. Pero sí, suena bastante extraño que pueda declararlo finalen la interfaz, pero que no sea definitivo en la implementación. Habría tenido más sentido si (a) la final palabra clave no estaba permitida para los argumentos del método de interfaz (abstracto) (pero puede usarla en la implementación), o (b) declarar un argumento como finalen la interfaz obligaría a declararlo finalen la implementación (pero no forzado para no finales).
ADTC
"Realmente no es una respuesta correcta a la pregunta" Tienes razón, no sé lo que estaba pensando ... debe haber sido las primeras horas de julio o algo así. :)
ADTC
2

Creo que puede ser un detalle superfluo, ya que si es final o no es un detalle de implementación.

(Algo así como declarar métodos / miembros en una interfaz como pública).

Peter
fuente