Letzte Argumente in Schnittstellenmethoden - worum geht es?

189

In Java ist es völlig legal, finalArgumente in Schnittstellenmethoden zu definieren und diese in der implementierenden Klasse nicht zu beachten, z.

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

Im obigen Beispiel barund bazhat die entgegengesetzten finalDefinitionen in der Klasse VS die Schnittstelle.

Auf die gleiche Weise werden keine finalEinschränkungen erzwungen, wenn eine Klassenmethode eine andere erweitert abstractoder nicht.

Während finaleinigen praktischen Wert innerhalb der Klassenmethode Körper hat, gibt es eine Stelle spezifiziert finalfür Schnittstellenmethodenparameter?

mindas
quelle
finalmacht sowieso nichts mit nativen Typen, da sie kopiert werden.
Paul Tomblin
11
Nur als Diskussionspunkt: Ich habe es gerade versucht, und wenn zwei interfaceDefinitionen nur im finalAttribut eines Arguments variieren , sind die resultierenden .classDateien Byte für Byte identisch (und javap -verzeugen natürlich dieselbe Ausgabe). Das gleiche gilt übrigens für zwei Klassen, die sich nur in finaleinem Attribut unterscheiden !
Joachim Sauer
2
@Paul: Es macht genau das Gleiche wie bei Referenztypen: Es verhindert, dass die Argumente selbst geändert werden (wenn sie in der Implementierung verwendet werden).
Joachim Sauer
Es ist in der Methodensignatur genauso relevant wie die Öffentlichkeit.
Robin
2
@Deepak: Ich sehe dich nach Arbeitsbeispielen für alle Arten von Fragen fragen, auch wenn es nicht viel Sinn macht. Ich denke, Sie sollten versuchen, etwas abstraktes Denken zu lernen: Denken Sie über ein Problem nach, ohne dass Sie ausführbaren Code vor sich haben. Es wird Ihnen auf lange Sicht sehr helfen.
Joachim Sauer

Antworten:

103

Es scheint keinen Sinn zu haben. Gemäß der Java-Sprachspezifikation 4.12.4 :

Das Deklarieren einer Variablen final kann als nützliche Dokumentation dafür dienen, dass sich ihr Wert nicht ändert, und kann dazu beitragen, Programmierfehler zu vermeiden.

Ein final Modifikator für einen Methodenparameter wird jedoch in den Regeln für den Abgleich von Signaturen überschriebener Methoden nicht erwähnt und hat keine Auswirkungen auf den Aufrufer, nur innerhalb des Hauptteils einer Implementierung. Wie Robin in einem Kommentar bemerkt hat, hat der finalModifikator für einen Methodenparameter keine Auswirkung auf den generierten Bytecode. (Dies gilt nicht für andere Verwendungen von final.)

Ted Hopp
quelle
Qualifiziert sich der Methodenparameter auch als Variable? Es ist offensichtlich in der Praxis, aber ist es im Spezifikationskontext?
Mindas
11
Es kann nicht zum Abgleichen von Signaturen verwendet werden, da es nicht in der tatsächlichen .class-Datei angezeigt wird. Es ist nur für den Compiler.
Robin
@mindas - das JLS sagt, dass es sieben Arten von Variablen gibt . Methodenparameter stehen an vierter Stelle der Liste.
Ted Hopp
3
Die Dokumentation ist jedoch finalnahezu nutzlos, da der Modifikator der implementierenden Klasse nicht erzwungen wird. Ihre Schnittstellensignatur kann einfach lügen.
Stefan Haberl
Die Java 8-Sprachspezifikation besagt nun, dass es acht Arten von Variablen gibt (von sieben - sie haben Lambda-Parameter hinzugefügt ). Methodenparameter stehen immer noch an vierter Stelle auf der Liste (zumindest einige Dinge im Leben scheinen stabil zu sein. :-)).
Ted Hopp
25

Einige IDEs kopieren die Signatur der Abstract- / Interface-Methode, wenn eine Implementierungsmethode in eine Unterklasse eingefügt wird.

Ich glaube nicht, dass es für den Compiler einen Unterschied macht.

EDIT: Obwohl ich glaube, dass dies in der Vergangenheit der Fall war, glaube ich nicht, dass aktuelle IDEs dies mehr tun.

Peter Lawrey
quelle
2
Gültiger Punkt, obwohl ich nicht glaube, dass es viele IDEs gab, als diese Funktion implementiert wurde (oder versehentlich verlassen wurde) :-)
mindas
2
Ich denke, ist in der Kategorie der static transientFelder. ;)
Peter Lawrey
1
Und öffentliche Konstruktoren einer abstrakten Klasse.
Peter Lawrey
1
IntelliJ macht das. Meine Version ist IntelliJ IDEA 2016.1.3 Build # IU-145.1617.
Siebenschläfer
1
@Dormouse, es ist interessant, weil Android Studio (3.1.4 Build # AI-173.4907809) nicht :(
The Godfather
18

Abschließende Anmerkungen zu Methodenparametern sind immer nur für die Methodenimplementierung relevant, niemals für den Aufrufer. Daher gibt es keinen wirklichen Grund, sie in Signaturen von Schnittstellenmethoden zu verwenden. Es sei denn, Sie möchten in allen Methodensignaturen denselben konsistenten Codierungsstandard befolgen, für den endgültige Methodenparameter erforderlich sind. Dann ist es schön, das zu können.

jmg
quelle
6

Update: Die ursprüngliche Antwort unten wurde geschrieben, ohne die Frage vollständig zu verstehen, und geht daher nicht direkt auf die Frage ein. :)Dennoch muss sie für diejenigen informativ sein, die die allgemeine Verwendung von verstehen möchtenfinal Schlüsselwörtern möchten.

Zu der Frage möchte ich meinen eigenen Kommentar von unten zitieren.

Ich glaube, Sie sind nicht gezwungen, die Endgültigkeit eines Arguments umzusetzen, damit Sie frei entscheiden können, ob es in Ihrer eigenen Umsetzung endgültig sein soll oder nicht.

Aber ja, es klingt ziemlich seltsam, dass Sie es finalin der Schnittstelle deklarieren können, es aber in der Implementierung nicht endgültig haben . Es wäre sinnvoller gewesen, wenn entweder:

ein. finalDas Schlüsselwort war für Argumente der Schnittstellenmethode (abstrakt) nicht zulässig (Sie können es jedoch in der Implementierung verwenden), oder
b. Das Deklarieren eines Arguments wie finalin der Schnittstelle würde die Deklaration finalin der Implementierung erzwingen (jedoch nicht für Nicht-Finale erzwingen ).


Ich kann mir zwei Gründe vorstellen, warum eine Methodensignatur finalParameter haben kann: Beans und Objekte ( Eigentlich sind beide der gleiche Grund, aber leicht unterschiedliche Kontexte. )

Objekte:

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

Das finalSchlüsselwort stellte sicher, dass wir nicht versehentlich einen neuen lokalen Kochtopf erstellen, indem bei diesem Versuch ein Kompilierungsfehler angezeigt wurde. Dies stellte sicher, dass die Hühnerbrühe in unseren ursprünglichen Kochtopf gegeben wurde, den die addChickenMethode erhielt. Vergleichen Sie dies mit der Stelle, addVegetablesan der wir den Blumenkohl verloren haben, weil er diesen anstelle des ursprünglichen Topfes in einen neuen lokalen Kochtopf gegeben hat.

Bohnen: Es ist das gleiche Konzept wie bei Objekten (wie oben gezeigt) . Bohnen sind im Wesentlichen Objects in Java. Beans (JavaBeans) werden jedoch in verschiedenen Anwendungen verwendet, um eine definierte Sammlung verwandter Daten bequem zu speichern und weiterzugeben. So wie das addVegetablesden Kochprozess durcheinander bringen könnte, indem ein neuer Kochtopf erstellt StringBuilderund mit dem Blumenkohl weggeworfen wird, könnte dies auch mit einem Kochtopf JavaBean geschehen .

ADTC
quelle
OK. Es ist nicht wirklich eine richtige Antwort auf die Frage (da ich nicht gezwungen bin, meine Implementierung der Schnittstellenmethode dazu zu bringen, endgültige Argumente anzunehmen, selbst wenn die Schnittstelle dies sagt), aber es ist trotzdem hilfreich, da es eine nette Erklärung dafür ist, warum Es ist zunächst eine gute Idee, die Methodenparameter endgültig zu machen.
Phil
Ich glaube, Sie sind nicht gezwungen, die Endgültigkeit eines Arguments umzusetzen, damit Sie frei entscheiden können, ob es in Ihrer eigenen Umsetzung endgültig sein soll oder nicht. Aber ja, es klingt ziemlich seltsam, dass Sie es finalin der Schnittstelle deklarieren können, es aber in der Implementierung nicht endgültig haben . Es wäre sinnvoller gewesen, wenn entweder (a) das final Schlüsselwort für Argumente der Schnittstellenmethode (abstrakt) nicht zulässig wäre (Sie können es jedoch in der Implementierung verwenden) oder (b) das Deklarieren eines Arguments wie finalin der Schnittstelle das Deklarieren finalin der Implementierung erzwingen würde (aber nicht für Nicht-Finale gezwungen).
ADTC
"Es ist nicht wirklich eine richtige Antwort auf die Frage." Sie haben Recht, ich weiß nicht, was ich dachte ... muss die frühen frühen Julistunden gewesen sein oder so. :)
ADTC
2

Ich glaube, es kann ein überflüssiges Detail sein, denn ob es endgültig ist oder nicht, ist ein Implementierungsdetail.

(So ​​ähnlich wie das Deklarieren von Methoden / Mitgliedern in einer Schnittstelle als öffentlich.)

Peter
quelle