459 Stimmen

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

Ich kann nicht verstehen, wo die final Schlüsselwort ist wirklich praktisch, wenn es für Methodenparameter verwendet wird.

Wenn wir die Verwendung anonymer Klassen, die Lesbarkeit und die Absichtserklärung ausklammern, scheint es mir fast wertlos zu sein.

Zu erzwingen, dass einige Daten konstant bleiben, ist nicht so einfach, wie es scheint.

  • Handelt es sich bei dem Parameter um ein Primitivum, hat dies keine Auswirkungen, da der Parameter als Wert an die Methode übergeben wird und seine Änderung keine Auswirkungen außerhalb des Anwendungsbereichs hat.

  • Wenn wir einen Parameter per Verweis übergeben, dann ist der Verweis selbst eine lokale Variable, und wenn der Verweis innerhalb der Methode geändert wird, hat das keine Auswirkungen außerhalb des Methodenbereichs.

Betrachten Sie das folgende einfache Testbeispiel. Dieser Test ist erfolgreich, obwohl die Methode den Wert des ihr übergebenen Verweises geändert hat, er hat also keine Auswirkungen.

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

0voto

Michael Rutherfurd Punkte 13367

Ein weiterer Grund für das Hinzufügen von final zu Parameterdeklarationen ist, dass es hilft, Variablen zu identifizieren, die im Rahmen eines "Extract Method"-Refactorings umbenannt werden müssen. Ich habe die Erfahrung gemacht, dass das Hinzufügen von final zu jedem Parameter vor dem Beginn einer großen Methodenumstrukturierung mir schnell zeigt, ob es Probleme gibt, die ich angehen muss, bevor ich fortfahre.

Ich entferne sie jedoch in der Regel am Ende des Refactorings als überflüssig.

-1voto

KunYu Tsai Punkte 542

Folgemaßnahmen zu Michels Beitrag. Ich habe mir ein weiteres Beispiel ausgedacht, um es zu erklären. Ich hoffe, es kann 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, in der Methode thisIsWhy() wir tatsächlich hat nicht zugewiesen el [Argument MyObj obj] zu einer wirkliche Referenz in MyParam. Stattdessen verwenden wir einfach die [Argument MyObj obj] in der Methode innerhalb von MyParam.

Aber nachdem wir die Methode beendet haben thisIsWhy() , Sollte das Argument (Objekt) MyObj noch existieren?

Scheint so zu sein, denn wie wir in main sehen können, rufen wir immer noch die Methode showObjName() und es muss erreichen obj . MyParam verwendet/erreicht das Methodenargument auch dann, wenn die Methode bereits zurückgegeben wurde!

Wie Java dies wirklich erreicht, ist die Erzeugung einer Kopie, die auch eine versteckte Referenz der Argument MyObj obj innerhalb des MyParam-Objekts (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.

Aber wenn wir das Argument nicht final gesetzt haben, was zu einer Situation führt, können wir einen neuen Speicher (Objekt) dem Argument MyObj obj .

Technisch gesehen gibt es überhaupt keinen Zusammenstoß! Wenn uns das erlaubt wird, sieht die Situation folgendermaßen aus:

  1. Wir haben jetzt ein verstecktes [MyObj obj], das auf ein [Memory A in heap] zeigt, das jetzt in MyParam-Objekt lebt.
  2. Wir haben auch eine andere [MyObj obj], die das Argument Punkt zu einem [Speicher B in Heap] jetzt live in thisIsWhy Methode ist.

Kein Zusammenstoß, aber "VERWIRREND!!" Denn sie verwenden alle die derselbe "Referenzname", nämlich "obj" .

Um dies zu vermeiden, setzen Sie es als "final", um zu verhindern, dass Programmierer den "fehleranfälligen" Code machen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X