it-swarm-eu.dev

Sollte ich in Java "final" für Parameter und Einheimische verwenden, auch wenn ich nicht muss?

Java ermöglicht das Markieren von Variablen (Felder/Lokale/Parameter) als final, um eine Neuzuweisung in diese zu verhindern. Ich finde es sehr nützlich bei Feldern, da es mir hilft, schnell zu erkennen, ob einige Attribute - oder eine ganze Klasse - unveränderlich sein sollen.

Andererseits finde ich es bei Einheimischen und Parametern viel weniger nützlich, und normalerweise vermeide ich es, sie als final zu markieren, selbst wenn sie niemals neu zugewiesen werden (mit der offensichtlichen Ausnahme, wenn dies erforderlich ist) in einer inneren Klasse verwendet). In letzter Zeit bin ich jedoch auf Code gestoßen, der, wann immer es möglich ist, final verwendet, was meiner Meinung nach technisch mehr Informationen liefert.

Ich bin mir meines Programmierstils nicht mehr sicher und frage mich, welche anderen Vor- und Nachteile es hat, final überall anzuwenden, was der gängigste Branchenstil ist und warum.

114
Oak

Ich benutze final genauso wie Sie. Für mich sieht es für lokale Variablen und Methodenparameter überflüssig aus und vermittelt keine nützlichen zusätzlichen Informationen.

Eine wichtige Sache ist, dass Sie sich bemühen, halten Sie meine Methoden kurz und sauber, wobei jede eine einzelne Aufgabe erledigt. Daher haben meine lokalen Variablen und Parameter einen sehr begrenzten Umfang und werden nur für einen einzigen Zweck verwendet. Dies minimiert die Wahrscheinlichkeit, dass sie versehentlich neu zugewiesen werden.

Darüber hinaus garantiert final nicht, dass Sie den Wert/Status einer (nicht primitiven) Variablen nicht ändern können. Nur, dass Sie die Referenz nach der Initialisierung nicht erneut diesem Objekt zuweisen können. Mit anderen Worten, es funktioniert nahtlos nur mit Variablen primitiven oder unveränderlichen Typs. Erwägen

final String s = "forever";
final int i = 1;
final Map<String, Integer> m = new HashMap<String, Integer>();

s = "never"; // compilation error!
i++; // compilation error!
m.put(s, i); // fine

Dies bedeutet, dass es in vielen Fällen immer noch nicht einfacher ist zu verstehen, was im Code passiert, und ein Missverständnis kann tatsächlich zu subtilen Fehlern führen, die schwer zu erkennen sind.

68
Péter Török

Ihr Java Programmierstil und Ihre Gedanken sind in Ordnung - Sie müssen dort nicht an sich zweifeln.

Andererseits finde ich es bei Einheimischen und Parametern viel weniger nützlich, und normalerweise vermeide ich es, sie als endgültig zu markieren, selbst wenn sie nie neu zugewiesen werden (mit der offensichtlichen Ausnahme, wenn sie in einer inneren Klasse verwendet werden müssen ).

Genau aus diesem Grund sollten Sie das Schlüsselwort final verwenden. Sie geben an, dass SIE wissen, dass es niemals neu zugewiesen wird, aber niemand anderes weiß das. Wenn Sie final verwenden, wird Ihr Code sofort ein bisschen mehr eindeutig.

66
J.K.

Ein Vorteil der Verwendung von final/const, wo immer dies möglich ist, besteht darin, dass die mentale Belastung für den Leser Ihres Codes verringert wird.

Er kann sicher sein, dass der Wert/die Referenz später nie mehr geändert wird. Er muss also nicht auf Änderungen achten, um die Berechnung zu verstehen.

Ich habe meine Meinung dazu geändert, nachdem ich rein funktionale Programmiersprachen gelernt habe. Junge, was für eine Erleichterung, wenn Sie einer "Variablen" vertrauen können, die immer ihren Anfangswert hält.

32

Ich betrachte final in Methodenparametern und lokalen Variablen als Code-Rauschen. Java Methodendeklarationen können sehr lang sein (insbesondere bei Generika) - sie müssen nicht mehr erstellt werden.

Wenn Unit-Tests richtig geschrieben sind, wird die Zuweisung von Parametern, die "schädlich" sind, übernommen, daher sollte es niemals tatsächlich ein Problem sein. Visuelle Klarheit ist wichtiger als das Vermeiden eines möglichen Fehlers, der nicht erkannt wird, weil Ihre Komponententests nicht ausreichend abgedeckt sind.

Tools wie FindBugs und CheckStyle, die so konfiguriert werden können, dass der Build unterbrochen wird, wenn Parameter oder lokale Variablen zugewiesen werden, wenn Sie sich intensiv mit solchen Dingen beschäftigen.

Wenn Sie brauchen, um sie endgültig zu machen, zum Beispiel, weil Sie den Wert in einer anonymen Klasse verwenden, dann ist das natürlich kein Problem - das ist die einfachste und sauberste Lösung.

Abgesehen von dem offensichtlichen Effekt, dass Sie Ihren Parametern zusätzliche Schlüsselwörter hinzufügen und diese meiner Meinung nach tarnen, kann das Hinzufügen von final zu Methodenparametern häufig dazu führen, dass der Code im Methodenkörper weniger lesbar wird, was den Code verschlechtert - um "guter" Code zu sein muss so lesbar und einfach wie möglich sein. Angenommen, ich habe eine Methode, bei der die Groß- und Kleinschreibung nicht berücksichtigt werden muss.

Ohne final:

public void doSomething(String input) {
    input = input.toLowerCase();
    // do a few things with input
}

Einfach. Sauber. Jeder weiß, was los ist.

Jetzt mit 'final', Option 1:

public void doSomething(final String input) {
    final String lowercaseInput = input.toLowerCase();
    // do a few things with lowercaseInput
}

Während die Parameter final den Codierer daran hindern, Code weiter unten hinzuzufügen, um zu glauben, dass er mit dem ursprünglichen Wert arbeitet, besteht das gleiche Risiko, dass Code weiter unten input anstelle von lowercaseInput verwendet , gegen die es nicht sollte und gegen die es nicht geschützt werden kann, weil Sie es nicht aus dem Geltungsbereich nehmen können (oder null sogar input zuweisen können, wenn dies überhaupt helfen würde).

Mit 'final' Option 2:

public void doSomething(final String input) {
    // do a few things with input.toLowerCase()
}

Jetzt haben wir nur noch mehr Code-Rauschen erzeugt und einen Performance-Hit eingeführt, bei dem toLowerCase() n-mal aufgerufen werden muss.

Mit 'final' Option 3:

public void doSomething(final String input) {
    doSomethingPrivate(input.toLowerCase());
}

/** @throws IllegalArgumentException if input not all lower case */
private void doSomethingPrivate(final String input) {
    if (!input.equals(input.toLowerCase())) {
        throw new IllegalArgumentException("input not lowercase");
    }
    // do a few things with input
}

Sprechen Sie über Code-Rauschen. Dies ist ein Zugunglück. Wir haben eine neue Methode, einen erforderlichen Ausnahmeblock, da anderer Code ihn möglicherweise falsch aufruft. Weitere Unit-Tests, um die Ausnahme abzudecken. Alles, um eine einfache und meiner Meinung nach bevorzugte und harmlose Linie zu vermeiden.

Es gibt auch das Problem, dass Methoden nicht so lang sein sollten, dass Sie sie nicht einfach visuell erfassen und auf einen Blick erkennen können, dass eine Zuweisung zu Parametern stattgefunden hat.

Ich denke, es ist eine gute Praxis/ein guter Stil, dass Sie, wenn Sie einem Parameter zuweisen, dies zu Beginn der Methode tun, vorzugsweise in der ersten Zeile oder direkt nach der grundlegenden Eingabeprüfung, effektiv ersetzen für das gesamt Methode, die innerhalb der Methode einen konsistenten Effekt hat. Die Leser erwarten, dass jede Zuweisung offensichtlich ist (in der Nähe der Signaturerklärung) und an einem konsistenten Ort, was das Problem, das durch das Hinzufügen von final zu vermeiden versucht wird, erheblich verringert. Eigentlich weise ich selten Parameter zu, aber wenn ich es tue, mache ich es immer am Anfang einer Methode.


Beachten Sie auch, dass final Sie nicht wirklich schützt, wie es zunächst scheinen mag:

public void foo(final Date date) {
    date.setTime(0); 
    // code that uses date
}

final schützt Sie nur dann vollständig, wenn der Parametertyp primitiv oder unveränderlich ist.

22
Bohemian

Ich habe Eclipse final vor jede lokale Variable setzen lassen, da ich der Meinung bin, dass dies das Lesen des Programms erleichtert. Ich mache es nicht mit Parametern, da ich die Parameterliste so kurz wie möglich halten möchte, sollte sie idealerweise in eine Zeile passen.

7
maaartinus