web-dev-qa-db-de.com

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

Ich kann nicht verstehen, wo das Schlüsselwort finalwirklich nützlich ist, wenn es für Methodenparameter verwendet wird.

Wenn wir die Verwendung anonymer Klassen, die Lesbarkeit und die Absichtserklärung ausschließen, erscheint es mir fast wertlos.

Das Erzwingen, 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 Gültigkeitsbereichs keine Auswirkung hat.

  • Wenn Sie einen Parameter als Referenz übergeben, ist die Referenz selbst eine lokale Variable. Wenn die Referenz innerhalb der Methode geändert wird, hat dies keine Auswirkungen außerhalb des Methodenbereichs.

Betrachten Sie das einfache Testbeispiel unten. Dieser Test ist bestanden, obwohl die Methode den Wert der ihr zugewiesenen 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;
}
362
Yotam Gilboa

Manchmal ist es schön, aus Gründen der Lesbarkeit explizit darauf hinzuweisen, dass sich die Variable nicht ändert. Hier ist ein einfaches Beispiel, in dem die Verwendung von final einige mögliche Kopfschmerzen ersparen kann:

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

Wenn Sie das Schlüsselwort 'this' in einem Setter vergessen, wird die Variable, die Sie festlegen möchten, nicht festgelegt. Wenn Sie jedoch das Schlüsselwort final für den Parameter verwenden, wird der Fehler beim Kompilieren abgefangen.

225
Jerry Sha

Beenden 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 zugewiesen wird.

Unabhängig davon, ob die Variable eine statische Variable, eine Elementvariable, eine lokale Variable oder eine Argument-/Parametervariable ist, ist der Effekt völlig derselbe.

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 Sie 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 verweist, markieren Sie die Variable final.

Weisen Sie niemals Argumente neu zu

Als gute Programmierpraxis (in jeder Sprache) sollten Sie nie einem anderen Objekt als dem von der aufrufenden Methode übergebenen Objekt eine Parameter-/Argumentvariable neu zuweisen. In den obigen Beispielen sollte man niemals die Zeile arg = Schreiben. Da Menschen Fehler machen und Programmierer Menschen sind, bitten wir den Compiler, uns zu helfen. Markieren Sie jede Parameter-/Argumentvariable als 'final', damit der Compiler solche Neuzuweisungen finden und kennzeichnen kann.

Retrospektiv

Wie bereits in anderen Antworten erwähnt ... Angesichts des ursprünglichen Entwurfsziels von Java, Programmierern dabei zu helfen, dumme Fehler wie das Lesen nach dem Ende eines Arrays zu vermeiden, sollte Java) so konzipiert sein, dass alle Parameter-/Argumentvariablen automatisch erzwungen werden mit anderen Worten Argumente sollten keine Variablen sein. Aber im Nachhinein ist es eine 20/20 Vision, und die Java) Designer hatten zu dieser Zeit alle Hände voll zu tun .

Der Vollständigkeit halber wurde ein weiterer Fall 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
 }
216
Basil Bourque

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

Persönlich neige ich dazu, final nicht für lokale Variablen und Parameter zu verwenden, es sei denn, ich verwende die Variable in einer anonymen inneren Klasse, aber ich kann mit Sicherheit den Punkt derer erkennen, die klarstellen möchten, dass der Parameter selbst ein Wert ist ändert sich nicht (auch wenn das Objekt, auf das es verweist, seinen Inhalt ändert). Für diejenigen, die dies für eine bessere Lesbarkeit halten, ist dies meines Erachtens völlig vernünftig.

Ihr Standpunkt wäre wichtiger, wenn jemand tatsächlich behaupten würde, dass es hat Daten auf eine Weise konstant hält, die es nicht tut - aber ich kann mich nicht erinnern, solche Behauptungen gesehen zu haben. Schlagen Sie vor, dass es eine große Zahl von Entwicklern gibt, die vorschlagen, dass final mehr Wirkung hat, als es tatsächlich der Fall ist?

EDIT: Ich hätte das alles wirklich mit einem Monty Python) -Verweis zusammenfassen sollen; die Frage scheint der Frage ähnlich zu sein: "Was haben die Römer jemals für uns getan?"

126
Jon Skeet

Lassen Sie mich etwas über den einen Fall erklären, in dem Sie haben final verwenden, den Jon bereits erwähnt hat:

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 final 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 müssen die Parameter from und to final 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 Stack und sind daher nur während der Ausführung der Methode vorhanden. Die anonyme Klasseninstanz wird jedoch von der Methode zurückgegeben, sodass sie möglicherweise viel länger gültig ist. 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 verborgene 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, die sie sieht, Änderungen an der Variablen vornehmen. Um die Illusion aufrechtzuerhalten, dass es nur eine Variable statt zwei Kopien gibt, muss sie endgültig sein.

75

Ich benutze die ganze Zeit Finale auf Parameter.

Fügt es so viel hinzu? Nicht wirklich.

Würde ich es ausschalten? Nein.

Der Grund: Ich habe 3 Bugs gefunden, bei denen Leute schlampigen Code geschrieben und keine Member-Variable in Accessors gesetzt haben. Alle Bugs waren schwer zu finden.

Ich würde gerne sehen, dass dies in einer zukünftigen Version von Java zum Standard gemacht wird. Die Pass-by-Value-/Referenz-Sache stößt auf eine Menge Nachwuchsprogrammierer.

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.

25
Fortyrunner

Die Verwendung von final in einem Methodenparameter hat nichts damit zu tun, was mit dem Argument auf der Aufruferseite geschieht. Es ist nur dazu gedacht, es innerhalb dieser Methode als unverändert zu kennzeichnen. Wenn ich versuche, einen funktionaleren Programmierstil zu verwenden, sehe ich den Wert darin.

18
Germán

Persönlich verwende ich final bei Methodenparametern nicht, weil es den Parameterlisten zu viel Unordnung verleiht. Ich ziehe es vor, zu erzwingen, dass Methodenparameter nicht durch etwas wie Checkstyle geändert werden.

Für lokale Variablen verwende ich, wann immer es möglich ist, final, ich lasse Eclipse dies sogar automatisch in meinem Setup für persönliche Projekte tun.

Ich hätte gerne etwas Stärkeres wie C/C++ const.

8
starblue

Da Java Kopien von Argumenten übergibt, ist die Relevanz meiner Meinung nach eher begrenzt. Ich denke, dies stammt aus der C++ - Ära, in der Sie verhindern könnten, dass der Referenzinhalt durch const char const *. Ich glaube, diese Art von Dingen lässt Sie glauben, dass der Entwickler von Natur aus dumm ist und vor wirklich jedem Charakter, den er schreibt, geschützt werden muss das final es sei denn, ich möchte nicht, dass jemand meine Methoden und Sachen außer Kraft setzt ... Vielleicht bin ich nur ein Entwickler der alten Schule ...: -O

4
Lawrence

Kurze Antwort: final hilft ein bisschen, aber ... verwendet stattdessen die defensive Programmierung auf der Clientseite.

Das Problem mit final ist, dass es nur die Referenz erzwingt, die unverändert ist, sodass die referenzierten Objektelemente ohne Wissen des Aufrufers mutiert werden können. Daher ist die beste Vorgehensweise in dieser Hinsicht die defensive Programmierung auf der Anruferseite, bei der unveränderliche Instanzen oder Kopien von Objekten erstellt werden, die von skrupellosen APIs überfallen werden könnten.

1
Darrell Teague

Ich benutze niemals final in einer Parameterliste, es fügt nur Unordnung hinzu, wie die vorherigen Befragten gesagt haben. Außerdem können Sie in Eclipse die Parametrierung so einstellen, dass ein Fehler generiert wird, sodass die Verwendung von final in einer Parameterliste für mich ziemlich überflüssig erscheint. Interessanterweise hat die Aktivierung der Eclipse-Einstellung für die Parametrierung, 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;
}

Devil's Advocate spielen, was genau ist daran falsch?

1
Chris Milburn

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 einer umfangreichen Umgestaltungsmethode schnell Aufschluss darüber gibt, ob es Probleme gibt, die behoben werden müssen, bevor ich fortfahre.

Im Allgemeinen entferne ich sie jedoch am Ende des Refactorings als überflüssig.

0