Ich kann nicht verstehen, wo das final
Schlüsselwort wirklich praktisch ist, wenn es für Methodenparameter verwendet wird.
Wenn wir die Verwendung anonymer Klassen, Lesbarkeit und Absichtserklärung ausschließen, erscheint es mir fast wertlos.
Die Durchsetzung, dass einige Daten konstant bleiben, ist nicht so stark, wie es scheint.
Wenn der Parameter ein Grundelement ist, hat er keine Auswirkung, da der Parameter als Wert an die Methode übergeben wird und eine Änderung außerhalb des Bereichs keine Auswirkung hat.
Wenn wir einen Parameter als Referenz übergeben, ist die Referenz selbst eine lokale Variable, und wenn die Referenz innerhalb der Methode geändert wird, hat dies keine Auswirkungen von außerhalb des Methodenbereichs.
Betrachten Sie das einfache Testbeispiel unten. Dieser Test besteht, obwohl die Methode den Wert der Referenz geändert hat, hat er keine Auswirkung.
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;
}
quelle
int foo(int* bar)
und der letzteint foo(int* &bar)
. Letzterer übergibt einen Zeiger als Referenz, Ersterer übergibt einen Verweis als Wert.Antworten:
Stoppen Sie die Neuzuweisung einer Variablen
Obwohl diese Antworten intellektuell interessant sind, habe ich die kurze einfache Antwort nicht gelesen:
Unabhängig davon, ob es sich bei der Variablen um eine statische Variable, eine Mitgliedsvariable, eine lokale Variable oder eine Argument- / Parametervariable handelt, ist der Effekt völlig gleich.
Beispiel
Lassen Sie uns den Effekt in Aktion sehen.
Betrachten Sie diese einfache Methode, bei der den beiden Variablen ( arg und x ) unterschiedliche Objekte zugewiesen werden können.
Markieren Sie die lokale Variable als final . Dies führt zu einem Compilerfehler.
Markieren wir stattdessen die Parametervariable als final . Auch dies führt zu einem Compilerfehler.
Moral der Geschichte:
Argumente niemals neu zuweisen
Als gute Programmierpraxis (in jeder Sprache) sollten Sie einem anderen Objekt als dem von der aufrufenden Methode übergebenen Objekt niemals eine Parameter- / Argumentvariable neu zuweisen. In den obigen Beispielen sollte man niemals die Zeile schreiben
arg =
. Da Menschen Fehler machen und Programmierer Menschen sind, bitten wir den Compiler, uns zu unterstützen. Markieren Sie jede Parameter- / Argumentvariable als 'final', damit der Compiler solche Neuzuweisungen finden und kennzeichnen kann.Im Rückblick
Wie in anderen Antworten erwähnt ... Angesichts des ursprünglichen Entwurfsziels von Java, Programmierern dabei zu helfen, dumme Fehler wie das Lesen über das Ende eines Arrays hinaus zu vermeiden, sollte Java so konzipiert sein, dass alle Parameter- / Argumentvariablen automatisch als "endgültig" erzwungen werden. Mit anderen Worten, Argumente sollten keine Variablen sein . Aber im Nachhinein ist 20/20 Vision, und die Java-Designer hatten zu dieser Zeit alle Hände voll zu tun.
Also immer
final
zu allen Argumenten hinzufügen ?Sollten wir
final
zu jedem deklarierten Methodenparameter hinzufügen ?➥
final
Nur hinzufügen, wenn der Code der Methode lang oder kompliziert ist und das Argument möglicherweise mit einer lokalen oder Mitgliedsvariablen verwechselt und möglicherweise neu zugewiesen wird.Wenn Sie sich für die Praxis entscheiden, niemals ein Argument neu zuzuweisen, neigen Sie dazu
final
, jedem ein Argument hinzuzufügen . Dies ist jedoch langwierig und erschwert das Lesen der Erklärung.Für kurzen einfachen Code, bei dem das Argument offensichtlich ein Argument ist und weder eine lokale Variable noch eine Mitgliedsvariable, mache ich mir nicht die Mühe, das hinzuzufügen
final
. Wenn der Code ganz offensichtlich ist und keine Chance besteht, dass ich oder ein anderer Programmierer versehentlich die Argumentvariable als etwas anderes als ein Argument verwechselt oder umgestaltet, dann stören Sie sich nicht. In meiner eigenen Arbeit füge ichfinal
nur längeren oder komplexeren Code hinzu, bei dem ein Argument möglicherweise mit einer lokalen oder Mitgliedsvariablen verwechselt wird.Ein weiterer Fall wurde der Vollständigkeit halber hinzugefügt
quelle
message = ( msg || 'Hello World"' )
. Es gibt einfach keinen Grund, keine separate Variable zu verwenden. Die einzigen Kosten sind ein paar Bytes Speicher.message = ( msg || 'Hello World"' )
Risiken riskiere ich später versehentlichmsg
. Wenn der Vertrag, den ich beabsichtige, lautet: "Verhalten ohne / null / undefiniertes Argument ist nicht vom Bestehen zu unterscheiden"Hello World"
", ist es eine gute Programmierpraxis, sich früh in der Funktion darauf festzulegen. [Dies kann ohne Neuzuweisung erreicht werden, indem mit begonnen wirdif (!msg) return myfunc("Hello World");
, dies wird jedoch mit mehreren Argumenten unhandlich.] In den seltenen Fällen, in denen es der Logik in der Funktion wichtig sein sollte, ob die Standardeinstellung verwendet wurde, würde ich lieber einen speziellen Sentinel-Wert festlegen (vorzugsweise öffentlich). .message
undmsg
. Aber wenn er es so etwasprocessedMsg
oder etwas anderes nennen würde, das zusätzlichen Kontext bietet, ist die Wahrscheinlichkeit eines Fehlers viel geringer. Konzentrieren Sie sich auf das, was er sagt, nicht auf das "Wie", das er sagt. ;)Manchmal ist es schön, explizit (aus Gründen der Lesbarkeit) zu sagen, dass sich die Variable nicht ändert. Hier ist ein einfaches Beispiel, bei dem die Verwendung
final
einige mögliche Kopfschmerzen ersparen kann:Wenn Sie das Schlüsselwort 'this' für einen Setter vergessen, wird die Variable, die Sie festlegen möchten, nicht festgelegt. Wenn Sie jedoch das
final
Schlüsselwort für den Parameter verwenden, wird der Fehler beim Kompilieren abgefangen.quelle
Ja, ohne anonyme Klassen, Lesbarkeit und Absichtserklärung ist es fast wertlos. Sind diese drei Dinge jedoch wertlos?
Persönlich neige ich dazu, nicht
final
für lokale Variablen und Parameter zu verwenden, es sei denn, ich verwende die Variable in einer anonymen inneren Klasse, aber ich kann sicherlich den Punkt derer erkennen, die klarstellen möchten, dass sich der Parameterwert selbst nicht ändert (auch nicht) wenn das Objekt, auf das es sich bezieht, seinen Inhalt ändert). Für diejenigen, die feststellen, dass dies die Lesbarkeit erhöht, halte ich es für eine völlig vernünftige Sache.Ihr Punkt wäre wichtig , wenn jemand tatsächlich behauptet , dass sie hätte Daten konstant in einer Art und Weise zu halten , dass es nicht - aber ich kann mich nicht erinnern, solche Ansprüche zu sehen. Schlagen Sie vor, dass eine beträchtliche Anzahl von Entwicklern vorschlägt, dass dies
final
mehr Wirkung hat als es wirklich tut?EDIT: Ich hätte das alles wirklich mit einer Monty Python-Referenz zusammenfassen sollen. Die Frage scheint etwas ähnlich zu sein wie die Frage: "Was haben die Römer jemals für uns getan?"
quelle
Lassen Sie mich etwas über den einen Fall erklären, in dem Sie sich befinden endgültig zu verwenden, die Jon bereits erwähnt:
Wenn Sie in Ihrer Methode eine anonyme innere Klasse erstellen und eine lokale Variable (z. B. einen Methodenparameter) in dieser Klasse verwenden, werden Sie vom Compiler gezwungen, den Parameter endgültig zu machen:
Hier das
from
undto
Parameter endgültig sein, damit sie innerhalb der anonymen Klasse verwendet werden können.Der Grund für diese Anforderung ist folgender: Lokale Variablen befinden sich auf dem Stapel, daher existieren sie nur, während die Methode ausgeführt wird. Die anonyme Klasseninstanz wird jedoch von der Methode zurückgegeben, sodass sie möglicherweise viel länger lebt. Sie können den Stapel nicht beibehalten, da er für nachfolgende Methodenaufrufe benötigt wird.
Java fügt stattdessen Kopien dieser lokalen Variablen als versteckte Instanzvariablen in die anonyme Klasse ein (Sie können sie sehen, wenn Sie den Bytecode untersuchen). Wenn sie jedoch nicht endgültig wären, könnte man erwarten, dass die anonyme Klasse und die Methode Änderungen sehen, die die andere an der Variablen vornimmt. Um die Illusion aufrechtzuerhalten, dass es nur eine Variable statt zwei Kopien gibt, muss sie endgültig sein.
quelle
final
in den Augen von HotSpot einen Unterschied zwischen einem Funktionsparameter und einem nicht endgültigen Funktionsparameter gibt?Ich benutze final die ganze Zeit für Parameter.
Fügt es so viel hinzu? Nicht wirklich.
Würde ich es ausschalten? Nein.
Der Grund: Ich habe 3 Fehler gefunden, bei denen Leute schlampigen Code geschrieben und keine Mitgliedsvariable in Accessors festgelegt haben. Alle Fehler waren schwer zu finden.
Ich würde gerne sehen, dass dies in einer zukünftigen Version von Java zum Standard gemacht wurde. Das Pass-by-Value / Referenz-Ding stolpert über eine Menge Junior-Programmierer.
Eine weitere Sache. Meine Methoden haben in der Regel eine geringe Anzahl von Parametern, sodass der zusätzliche Text in einer Methodendeklaration kein Problem darstellt.
quelle
Die Verwendung von final in einem Methodenparameter hat nichts damit zu tun, was mit dem Argument auf der Aufruferseite passiert. Es soll nur markieren, dass es sich innerhalb dieser Methode nicht ändert. Wenn ich versuche, einen funktionaleren Programmierstil zu verwenden, sehe ich den Wert darin.
quelle
final
.Persönlich verwende ich final nicht für Methodenparameter, da es den Parameterlisten zu viel Unordnung verleiht. Ich ziehe es vor, durchzusetzen, dass Methodenparameter nicht durch etwas wie Checkstyle geändert werden.
Für lokale Variablen, die ich nach Möglichkeit final verwende, lasse ich Eclipse dies sogar automatisch in meinem Setup für persönliche Projekte tun.
Ich hätte sicherlich gerne etwas Stärkeres wie C / C ++ const.
quelle
Da Java Kopien von Argumenten übergibt, ist die Relevanz von Java meiner Meinung nach
final
eher begrenzt. Ich denke, die Gewohnheit stammt aus der C ++ - Ära, in der Sie verhindern könnten, dass Referenzinhalte durch a geändert werdenconst char const *
. Ich glaube, diese Art von Sachen lässt Sie glauben, dass der Entwickler von Natur aus so dumm ist und vor wirklich jedem Charakter geschützt werden muss, den er tippt. In aller Demut darf ich sagen, ich schreibe nur sehr wenige Fehler, obwohl ich sie weglassefinal
(es sei denn, ich möchte nicht, dass jemand meine Methoden und Klassen außer Kraft setzt). Vielleicht bin ich nur ein Entwickler der alten Schule.quelle
Ich verwende final nie in einer Parameterliste, es fügt nur Unordnung hinzu, wie frühere Befragte gesagt haben. Auch in Eclipse können Sie die Parameterzuweisung so einstellen, dass ein Fehler generiert wird, sodass die Verwendung von final in einer Parameterliste für mich ziemlich überflüssig erscheint. Interessanterweise hat dieser Code beim Aktivieren der Eclipse-Einstellung für die Parameterzuweisung, die einen Fehler generiert, diesen Code abgefangen (so erinnere ich mich nur an den Ablauf, nicht an den tatsächlichen Code): -
Was genau ist daran falsch, wenn man Devil's Advocate spielt?
quelle
Kurze Antwort:
final
Hilft ein bisschen, aber ... verwenden Sie stattdessen defensive Programmierung auf der Client-Seite.In der Tat besteht das Problem
final
darin, dass nur die Referenz erzwungen wird, die unverändert bleibt, sodass die referenzierten Objektmitglieder ohne Wissen des Anrufers mutiert mutiert werden können. Daher ist die beste Vorgehensweise in dieser Hinsicht die defensive Programmierung auf der Aufruferseite, bei der tief unveränderliche Instanzen oder tiefe Kopien von Objekten erstellt werden, die von skrupellosen APIs überfallen werden könnten.quelle
Ein weiterer Grund für das Hinzufügen von final zu Parameterdeklarationen besteht darin, dass Variablen identifiziert werden können, die im Rahmen eines Refactorings "Extract Method" umbenannt werden müssen. Ich habe festgestellt, dass das Hinzufügen von final zu jedem Parameter vor dem Starten eines großen Methoden-Refactorings schnell anzeigt, ob es Probleme gibt, die ich beheben muss, bevor ich fortfahre.
Im Allgemeinen entferne ich sie jedoch am Ende des Refactorings als überflüssig.
quelle
Folgen Sie dem Beitrag von Michel. Ich selbst habe mir ein weiteres Beispiel gemacht, um es zu erklären. Ich hoffe es könnte helfen.
Aus dem obigen Code wird bei dem Verfahren thisIsWhy () , wir tatsächlich haben zuweisen nicht das [Argument MyObj obj] zu einer echten Referenz in MyParam. Stattdessen verwenden wir einfach das [Argument MyObj obj] in der Methode in MyParam.
Aber nachdem wir das Verfahren beenden thisIsWhy () , sollte das Argument (Objekt) MyObj noch?
Scheint so, als ob es sollte, weil wir in main sehen können, dass wir immer noch die Methode showObjName () aufrufen und sie obj erreichen muss . MyParam verwendet / erreicht weiterhin das Methodenargument, auch wenn die Methode bereits zurückgegeben wurde!
Wie Java dies wirklich erreicht, indem es eine Kopie generiert, ist auch eine versteckte Referenz des Arguments MyObj obj im MyParam-Objekt (aber es ist kein formales Feld in MyParam, so dass wir es nicht sehen können).
Wenn wir "showObjName" aufrufen, wird diese Referenz verwendet, um den entsprechenden Wert zu erhalten.
Wenn wir das Argument jedoch nicht endgültig setzen, was zu einer Situation führt, können wir dem Argument MyObj obj einen neuen Speicher (Objekt) zuweisen .
Technisch gibt es überhaupt keinen Konflikt! Wenn wir das dürfen, ist unten die Situation:
Kein Zusammenprall, aber "verwirrend !!" Weil sie alle denselben "Referenznamen" verwenden, der "obj" ist .
Um dies zu vermeiden, setzen Sie es auf "final", um zu vermeiden, dass Programmierer den "fehleranfälligen" Code ausführen.
quelle