Arguments finaux dans les méthodes d'interface - à quoi ça sert?

192

En Java, il est parfaitement légal de définir des finalarguments dans les méthodes d'interface et de ne pas obéir à cela dans la classe d'implémentation, par exemple:

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) {
        ...
    }
}

Dans l'exemple ci-dessus, baret baza les finaldéfinitions opposées dans la classe VS l'interface.

De la même manière, aucune finalrestriction n'est appliquée lorsqu'une méthode de classe en étend une autre, que ce soit abstractou non.

Bien que finalle corps de la méthode de classe ait une valeur pratique, y a-t-il un point précisant les finalparamètres de la méthode d'interface?

mindas
la source
finalne fait rien avec les types natifs de toute façon, puisqu'ils sont copiés.
Paul Tomblin
11
Juste comme point de discussion: je viens de l'essayer et si deux interfacedéfinitions ne varient que dans l' finalattribut d'un argument, alors les .classfichiers résultants sont octet pour octet de manière identique (et javap -vproduisent bien sûr la même sortie). La même chose est vraie pour deux classes qui ne diffèrent que finalsur un attribut, d'ailleurs!
Joachim Sauer
2
@Paul: il fait exactement la même chose qu'avec les types référence: il empêche les arguments eux-mêmes d'être modifiés (s'ils sont utilisés dans l'implémentation).
Joachim Sauer
Elle a autant de pertinence que le public dans la signature de la méthode.
Robin
2
@Deepak: Je vois que vous demandez des exemples de travail sur toutes sortes de questions, même si cela ne semble pas avoir beaucoup de sens. Je pense que vous devriez essayer d'apprendre une pensée abstraite: essayez de penser à un problème sans avoir de code exécutable devant vous. Cela vous aidera beaucoup à long terme.
Joachim Sauer

Réponses:

108

Il ne semble pas y avoir de raison. Selon la spécification du langage Java 4.12.4 :

Déclarer une variable final peut servir de documentation utile indiquant que sa valeur ne changera pas et peut aider à éviter des erreurs de programmation.

Cependant, un finalmodificateur sur un paramètre de méthode n'est pas mentionné dans les règles de correspondance des signatures de méthodes remplacées, et il n'a aucun effet sur l'appelant, uniquement dans le corps d'une implémentation. De plus, comme l'a noté Robin dans un commentaire, le finalmodificateur sur un paramètre de méthode n'a aucun effet sur le code d'octet généré. (Ce n'est pas vrai pour les autres utilisations de final.)

Ted Hopp
la source
Le paramètre de méthode est-il également qualifié de variable? C'est évidemment dans la pratique, mais est-ce dans un contexte de spécification?
mindas
11
Il ne peut pas être utilisé pour faire correspondre les signatures car il n'apparaît pas dans le fichier .class réel. C'est uniquement pour le compilateur.
Robin
@mindas - le JLS dit qu'il existe sept types de variables . Les paramètres de méthode sont quatrièmes sur la liste.
Ted Hopp
3
Cependant, la documentation est presque inutile, car le finalmodificateur n'est pas appliqué à la classe d'implémentation. Votre signature d'interface peut simplement mentir.
Stefan Haberl
La spécification du langage Java 8 indique maintenant qu'il existe huit types de variables (au lieu de sept - ils ont ajouté des paramètres lambda ). Les paramètres de méthode sont toujours quatrième sur la liste (au moins certaines choses dans la vie semblent stables. :-)).
Ted Hopp
25

Certains IDE copient la signature de la méthode abstraite / d'interface lors de l'insertion d'une méthode d'implémentation dans une sous-classe.

Je ne pense pas que cela fasse une différence pour le compilateur.

EDIT: Bien que je pense que c'était vrai dans le passé, je ne pense pas que les IDE actuels le fassent plus.

Peter Lawrey
la source
2
Point valide, bien que je ne pense pas qu'il y ait eu beaucoup d'IDE lorsque cette fonctionnalité a été implémentée (ou laissée accidentellement) :-)
mindas
2
Je pense que c'est dans la catégorie des static transientdomaines. ;)
Peter Lawrey
1
Et les constructeurs publics sur une classe abstraite.
Peter Lawrey
1
IntelliJ fait cela. Ma version est IntelliJ IDEA 2016.1.3 Build # IU-145.1617.
Loir du
1
@Dormouse, c'est intéressant, car Android Studio (3.1.4 Build # AI-173.4907809) ne fait pas :(
The Godfather
18

Les annotations finales des paramètres de méthode sont toujours pertinentes uniquement pour l'implémentation de la méthode, jamais pour l'appelant. Par conséquent, il n'y a aucune vraie raison de les utiliser dans les signatures de méthode d'interface. Sauf si vous souhaitez suivre la même norme de codage cohérente, qui nécessite des paramètres de méthode finaux, dans toutes les signatures de méthode. Alors c'est bien de pouvoir le faire.

jmg
la source
7

Mise à jour: La réponse originale ci-dessous a été rédigée sans bien comprendre la question, et ne répond donc pas directement à la question. :)Néanmoins, elle doit être informative pour ceux qui cherchent à comprendre l'utilisation générale du finalmot - clé.

Pour ce qui est de la question, je voudrais citer mon propre commentaire ci-dessous.

Je pense que vous n'êtes pas obligé de mettre en œuvre la finalité d'un argument pour vous laisser libre de décider s'il doit être définitif ou non dans votre propre mise en œuvre.

Mais oui, cela semble assez étrange que vous puissiez le déclarer finaldans l'interface, mais que ce soit non final dans l'implémentation. Cela aurait été plus logique si:

une. finalLe mot clé n'était pas autorisé pour les arguments de méthode d'interface (abstraite) (mais vous pouvez l'utiliser dans l'implémentation), ou
b. déclarer un argument comme finaldans interface le forcerait à être déclaré finaldans l'implémentation (mais pas forcé pour les non finales).


Je peux penser à deux raisons pour lesquelles une signature de méthode peut avoir des finalparamètres: Beans et Objects (en fait, ce sont tous les deux la même raison, mais des contextes légèrement différents. )

Objets:

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 ");
}

Le finalmot-clé garantit que nous ne créerons pas accidentellement une nouvelle marmite locale en affichant une erreur de compilation lorsque nous avons tenté de le faire. Cela garantit que le bouillon de poulet est ajouté à notre marmite d'origine que la addChickenméthode a obtenue. Comparez cela à l' addVegetablesendroit où nous avons perdu le chou-fleur parce qu'il l'a ajouté à une nouvelle marmite locale au lieu de la marmite d'origine.

Beans: C'est le même concept que les objets (comme indiqué ci-dessus) . Les beans sont essentiellement des Objects en Java. Cependant, les beans (JavaBeans) sont utilisés dans diverses applications comme un moyen pratique de stocker et de transmettre une collection définie de données associées. Tout comme le addVegetablespourrait gâcher le processus de cuisson en créant une nouvelle marmite StringBuilderet en le jetant avec le chou-fleur, il pourrait également faire de même avec une marmite JavaBean .

ADTC
la source
D'accord. Ce n'est pas vraiment une réponse correcte à la question (puisque je ne suis pas obligé de faire en sorte que mon implémentation de la méthode d'interface prenne les arguments finaux, même si l'interface le dit), mais c'est toujours utile, en ce sens que c'est une belle explication de pourquoi faire des paramètres de méthode finaux est une bonne idée pour commencer.
Phil
Je pense que vous n'êtes pas obligé de mettre en œuvre la finalité d'un argument pour vous laisser libre de décider s'il doit être définitif ou non dans votre propre mise en œuvre. Mais oui, cela semble assez étrange que vous puissiez le déclarer finaldans l'interface, mais que ce soit non final dans l'implémentation. Cela aurait été plus logique si (a) final mot-clé n'était pas autorisé pour les arguments de la méthode d'interface (abstraite) (mais vous pouvez l'utiliser dans l'implémentation), ou (b) déclarer un argument comme finaldans interface le forcerait à être déclaré finaldans l'implémentation (mais pas forcé pour les non-finales).
ADTC
"Ce n'est pas vraiment une réponse correcte à la question" Tu as raison, je ne sais pas à quoi je pensais ... ça devait être les petites heures de juillet ou quelque chose comme ça. :)
ADTC
2

Je pense que c'est peut-être un détail superflu, car le fait qu'il soit définitif ou non est un détail de mise en œuvre.

(Un peu comme déclarer des méthodes / membres dans une interface comme public.)

Peter
la source