Warum sollte ich das Schlüsselwort "final" für einen Methodenparameter in Java verwenden?

381

Ich kann nicht verstehen, wo das finalSchlü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;
}
Basil Bourque
quelle
101
Ein kurzer Punkt zur Terminologie: Java hat überhaupt keine Referenzübergabe. Es hat eine Passreferenz nach Wert, was nicht dasselbe ist. Bei einer echten Pass-by-Referenzsemantik wären die Ergebnisse Ihres Codes unterschiedlich.
Jon Skeet
6
Was ist der Unterschied zwischen Referenzübergabe und Referenzübergabe nach Wert?
NobleUplift
Es ist einfacher, diesen Unterschied in einem C-Kontext zu beschreiben (zumindest für mich). Wenn ich einen Zeiger auf eine Methode wie <code> int foo (int bar) </ code> übergebe, wird dieser Zeiger als Wert übergeben. Das heißt, es wird kopiert. Wenn ich also etwas in dieser Methode mache, wie <code> free (bar); bar = malloc (...); </ code> dann habe ich gerade eine wirklich schlechte Sache gemacht. Der kostenlose Anruf gibt tatsächlich den Speicherplatz frei, auf den gezeigt wird (also baumelt jetzt der Zeiger, den ich übergeben habe). <Code> int foo (int & bar) </ bar> bedeutet jedoch, dass der Code gültig ist und der Wert des übergebenen Zeigers geändert wird.
Jerlan
1
Der erste soll sein int foo(int* bar)und der letzte int foo(int* &bar). Letzterer übergibt einen Zeiger als Referenz, Ersterer übergibt einen Verweis als Wert.
Jerslan
2
@ Martin, meiner Meinung nach ist es eine gute Frage ; Siehe den Titel für die Frage und den Inhalt des Beitrags als Erklärung dafür, warum die Frage gestellt wird. Vielleicht verstehe ich die Regeln hier falsch, aber genau diese Frage wollte ich bei der Suche nach "Verwendungen von Endparametern in Methoden" .
Victor Zamanian

Antworten:

239

Stoppen Sie die Neuzuweisung einer Variablen

Obwohl diese Antworten intellektuell interessant sind, habe ich die kurze einfache Antwort nicht gelesen:

Verwenden Sie das Schlüsselwort final, wenn der Compiler verhindern soll, dass eine Variable einem anderen Objekt neu zugewiesen wird.

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.

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

Markieren Sie die lokale Variable als final . Dies führt zu einem Compilerfehler.

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

Markieren wir stattdessen die Parametervariable als final . Auch dies führt zu einem Compilerfehler.

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

Moral der Geschichte:

Wenn Sie sicherstellen möchten, dass eine Variable immer auf dasselbe Objekt zeigt, markieren Sie die Variable final .

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 finalzu allen Argumenten hinzufügen ?

Sollten wir finalzu jedem deklarierten Methodenparameter hinzufügen ?

  • Theoretisch ja.
  • In der Praxis nein.
    finalNur 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 ich finalnur 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

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }
Basil Bourque
quelle
23
"Als gute Programmierpraxis (in jeder Sprache) sollten Sie niemals eine Parameter- / Argumentvariable [..] neu zuweisen." Entschuldigung, ich muss Sie in diesem Fall wirklich anrufen. Das Neuzuweisen von Argumenten ist in Sprachen wie Javascript Standard, in denen die Anzahl der übergebenen Argumente (oder auch wenn sie übergeben wurden) nicht durch die Methodensignatur vorgegeben wird. Zum Beispiel mit einer Signatur wie: "function say (msg)" stellen die Leute sicher, dass das Argument 'msg' zugewiesen ist, wie folgt: "msg = msg || 'Hello World!';". Die besten Javascript-Programmierer der Welt brechen Ihre bewährten Verfahren. Lesen Sie einfach die jQuery-Quelle.
Stijn de Witt
37
@StijndeWitt Ihr Beispiel zeigt genau das Problem der Neuzuweisung der Argumentvariablen. Sie verlieren Informationen, ohne dass Sie dafür etwas gewonnen haben : (a) Sie haben den übergebenen ursprünglichen Wert verloren. (B) Sie haben die Absicht der aufrufenden Methode verloren. (Hat der Anrufer "Hallo Welt!" Sowohl a als auch b sind nützlich zum Testen von langem Code und wenn der Wert später weiter geändert wird. Ich stehe zu meiner Aussage: arg vars sollte niemals neu zugewiesen werden. Ihr Code sollte sein : message = ( msg || 'Hello World"' ). Es gibt einfach keinen Grund, keine separate Variable zu verwenden. Die einzigen Kosten sind ein paar Bytes Speicher.
Basil Bourque
9
@Basil: Es zählt mehr Code (in Bytes) und in Javascript. Schwer. Wie bei vielen Dingen basiert es auf Meinungen. Es ist durchaus möglich, diese Programmierpraxis vollständig zu ignorieren und dennoch exzellenten Code zu schreiben. Die Programmierpraxis einer Person macht es nicht zu jeder Übung. Steh zu allem, was du willst, ich entscheide mich trotzdem, es anders zu schreiben. Macht mich das zu einem schlechten Programmierer oder zu meinem Code zu einem schlechten Code?
Stijn de Witt
14
Mit message = ( msg || 'Hello World"' )Risiken riskiere ich später versehentlich msg. 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 wird if (!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). .
Beni Cherniavsky-Paskin
6
@ BeniCherniavsky-Paskin das Risiko, das Sie beschreiben, ist nur wegen der Ähnlichkeit zwischen messageund msg. Aber wenn er es so etwas processedMsgoder 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. ;)
Alfasin
230

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 finaleinige mögliche Kopfschmerzen ersparen kann:

public void setTest(String test) {
    test = test;
}

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 finalSchlüsselwort für den Parameter verwenden, wird der Fehler beim Kompilieren abgefangen.

Jerry Sha
quelle
65
Übrigens sehen Sie die Warnung "Die Zuordnung zum Variablentest hat sowieso keine Auswirkung"
AvrDragon
12
@ AvrDragon Aber wir könnten die Warnung auch ignorieren. Es ist also immer besser, etwas zu haben, das uns davon abhält, weiter zu gehen, wie einen Kompilierungsfehler, den wir mit dem letzten Schlüsselwort erhalten.
Sumit Desai
10
@ AvrDragon Das hängt von der Entwicklungsumgebung ab. Sie sollten sich sowieso nicht auf die IDE verlassen, um solche Dinge für Sie zu fangen, es sei denn, Sie möchten schlechte Gewohnheiten entwickeln.
b1nary.atr0phy
25
@ b1naryatr0phy eigentlich ist es eine Compiler-Warnung, nicht nur IDE-Tipp
AvrDragon
8
@SumitDesai "Aber wir könnten die Warnung auch ignorieren. Es ist also immer besser, etwas zu haben, das uns davon abhält, weiter zu gehen, wie einen Kompilierungsfehler, den wir mit dem letzten Schlüsselwort erhalten." Ich verstehe Ihren Standpunkt, aber dies ist eine sehr starke Aussage, mit der viele Java-Entwickler meiner Meinung nach nicht einverstanden wären. Compiler-Warnungen gibt es aus einem bestimmten Grund, und ein kompetenter Entwickler sollte keinen Fehler benötigen, um sie zu zwingen, ihre Auswirkungen zu berücksichtigen.
Stuart Rossiter
127

Ja, ohne anonyme Klassen, Lesbarkeit und Absichtserklärung ist es fast wertlos. Sind diese drei Dinge jedoch wertlos?

Persönlich neige ich dazu, nicht finalfü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 finalmehr 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?"

Jon Skeet
quelle
17
Aber um Krusty mit seinem Dänisch zu paraphrasieren, was haben sie SPÄT für uns getan ? =)
James Schek
Yuval. Das ist lustig! Ich denke, Frieden kann passieren, selbst wenn er durch eine Schwertklinge erzwungen wird!
Gonzobrains
1
Die Frage scheint eher der Frage zu ähneln: "Was haben die Römer nicht für uns getan?", Weil es eher eine Kritik daran ist, was das letzte Schlüsselwort nicht tut.
NobleUplift
"Schlagen Sie vor, dass es eine beträchtliche Anzahl von Entwicklern gibt, die behaupten, dass final mehr Wirkung hat als es wirklich tut?" Für mich ist das das Hauptproblem: Ich vermute sehr, dass ein erheblicher Teil der Entwickler, die es verwenden, der Meinung ist, dass es die Unveränderlichkeit der übergebenen Elemente des Anrufers erzwingt , wenn dies nicht der Fall ist. Natürlich wird man dann in die Debatte hineingezogen, ob Kodierungsstandards vor konzeptionellen Missverständnissen "schützen" sollten (die ein "kompetenter" Entwickler kennen sollte) oder nicht (und dies führt dann zu Meinungen außerhalb des SO-Bereichs -Typ Frage)!
Stuart Rossiter
1
@SarthakMittal: Der Wert wird nur kopiert, wenn Sie ihn tatsächlich verwenden, wenn Sie sich das fragen.
Jon Skeet
76

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:

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

Hier das fromundto 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.

Michael Borgwardt
quelle
1
Du hast mich aus "Aber wenn sie nicht endgültig sind ..." verloren. Kannst du versuchen, es neu zu formulieren? Vielleicht habe ich nicht genug Kaffee gehabt.
Hhafez
1
Sie haben eine lokale Variable von - die Frage ist, was passiert, wenn Sie die anon-Klasseninstanz innerhalb der Methode verwenden und die Werte von from ändern. Die Benutzer würden erwarten, dass die Änderung in der Methode sichtbar ist, da sie nur eine Variable sehen. Um diese Verwirrung zu vermeiden, muss sie endgültig sein.
Michael Borgwardt
Es wird keine Kopie erstellt, sondern lediglich ein Verweis auf das Objekt, auf das verwiesen wurde.
Vickirk
1
@vickirk: sicher, dass es eine Kopie der Referenz erstellt, bei Referenztypen.
Michael Borgwardt
Unter der Annahme, dass wir keine anonymen Klassen haben, die auf diese Variablen verweisen, wissen Sie, ob es finalin den Augen von HotSpot einen Unterschied zwischen einem Funktionsparameter und einem nicht endgültigen Funktionsparameter gibt?
Pacerier
25

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.

Fortyrunner
quelle
4
Ich wollte dies auch vorschlagen, dass final in zukünftigen Versionen die Standardeinstellung ist und dass Sie ein "veränderbares" oder besseres Schlüsselwort angeben müssen, das konzipiert ist. Hier ist ein schöner Artikel dazu: lpar.ath0.com/2008/08/26/java-annoyance-final-parameters
Jeff Axelrod
Es ist lange her, aber könnten Sie ein Beispiel für den Fehler geben, den Sie entdeckt haben?
user949300
Sehen Sie die am häufigsten gewählte Antwort. Das ist ein wirklich gutes Beispiel, bei dem die Mitgliedsvariable nicht gesetzt ist und stattdessen der Parameter mutiert ist.
Fortyrunner
18

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.

Deutsche
quelle
2
Genau, es ist nicht Teil der Funktionsschnittstelle, sondern nur der Implementierung. Es ist verwirrend, dass Java Parameter in Schnittstellen / abstrakten Methodendeklarationen zulässt (aber dirsregards) final .
Beni Cherniavsky-Paskin
8

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.

Sternenblau
quelle
Nicht sicher, ob IDE- und Tool-Referenzen für die OP-Veröffentlichung oder das Thema gelten. "Final" ist nämlich eine Überprüfung zur Kompilierungszeit, ob die Referenz nicht geändert / überfallen wird. Um solche Dinge wirklich durchzusetzen, siehe Antwort über keinen Schutz für Kinder von endgültigen Referenzen. Wenn Sie beispielsweise eine API erstellen, hilft die Verwendung einer IDE oder eines Tools externen Parteien nicht, solchen Code zu verwenden / zu erweitern.
Darrell Teague
4

Da Java Kopien von Argumenten übergibt, ist die Relevanz von Java meiner Meinung nach finaleher begrenzt. Ich denke, die Gewohnheit stammt aus der C ++ - Ära, in der Sie verhindern könnten, dass Referenzinhalte durch a geändert werden const 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 weglasse final(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.

Lawrence
quelle
1

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): -

private String getString(String A, int i, String B, String C)
{
    if (i > 0)
        A += B;

    if (i > 100)
        A += C;

    return A;
}

Was genau ist daran falsch, wenn man Devil's Advocate spielt?

Chris Milburn
quelle
Seien Sie dankbar, eine IDE von einer Laufzeit-JVM zu unterscheiden. Was auch immer eine IDE tut, ist irrelevant, wenn der kompilierte Bytecode auf dem Server ausgeführt wird, es sei denn, IDE hat Code hinzugefügt, um sich vor dem Überfallen von Mitgliedsvariablen zu schützen, wie z. B. einem Codefehler, als beabsichtigt war, dass eine Variable nicht neu zugewiesen werden sollte, sondern fälschlicherweise - daher der Zweck von letztes Schlüsselwort.
Darrell Teague
1

Kurze Antwort: finalHilft ein bisschen, aber ... verwenden Sie stattdessen defensive Programmierung auf der Client-Seite.

In der Tat besteht das Problem finaldarin, 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.

Darrell Teague
quelle
2
"Das Problem mit final ist, dass nur erzwungen wird, dass die Referenz unverändert bleibt" - Unwahr, Java selbst verhindert dies. Bei einer an eine Methode übergebenen Variablen kann die Referenz durch diese Methode nicht geändert werden.
Madbreaks
Bitte recherchieren Sie vor dem Posten ... stackoverflow.com/questions/40480/…
Darrell Teague
Einfach ausgedrückt, wenn es wahr wäre, dass Verweise auf Verweise nicht geändert werden können, würde es keine Diskussion über defensives Kopieren, Unveränderlichkeit, keine Notwendigkeit für ein endgültiges Schlüsselwort usw. geben
Darrell Teague
Entweder missverstehen Sie mich oder Sie irren sich. Wenn ich eine Objektreferenz an eine Methode übergebe und diese Methode sie neu zuweist, bleibt die ursprüngliche Referenz für (mich) den Aufrufer intakt, wenn die Methode ihre Ausführung abgeschlossen hat. Java ist streng pass-by-value. Und Sie sind dreist anmaßend zu behaupten, ich hätte keine Nachforschungen angestellt.
Madbreaks
Downvoting, weil op gefragt hat, warum final verwendet wird, und Sie nur einen falschen Grund angegeben haben.
Madbreaks
0

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.

Michael Rutherfurd
quelle
-1

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.

public static void main(String[] args){
    MyParam myParam = thisIsWhy(new MyObj());
    myParam.setArgNewName();

    System.out.println(myParam.showObjName());
}

public static MyParam thisIsWhy(final MyObj obj){
    MyParam myParam = new MyParam() {
        @Override
        public void setArgNewName() {
            obj.name = "afterSet";
        }

        @Override
        public String showObjName(){
            return obj.name;
        }
    };

    return myParam;
}

public static class MyObj{
    String name = "beforeSet";
    public MyObj() {
    }
}

public abstract static class MyParam{
    public abstract void setArgNewName();
    public abstract String showObjName();
}

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:

  1. Wir haben jetzt einen versteckten [MyObj obj] Punkt auf einen [Speicher A im Heap], der jetzt im MyParam-Objekt lebt.
  2. Wir haben auch ein anderes [MyObj obj], das das Argument für einen [Speicher B im Heap] ist, der jetzt in dieser IsWhy-Methode lebt.

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.

KunYu Tsai
quelle