Während der Entwicklungsphase gibt es bestimmte Variablen, die im selben Durchlauf korrigiert werden müssen, aber möglicherweise im Laufe der Zeit geändert werden müssen. Zum Beispiel ein boolean
, um den Debug-Modus zu signalisieren, damit wir Dinge im Programm tun, die wir normalerweise nicht tun würden.
Ist es ein schlechter Stil, diese Werte in einer Konstanten zu enthalten, dh final static int CONSTANT = 0
in Java? Ich weiß, dass eine Konstante während der Laufzeit gleich bleibt, aber soll sie auch während der gesamten Entwicklung gleich sein, außer natürlich ungeplanten Änderungen?
Ich habe nach ähnlichen Fragen gesucht, aber nichts gefunden, was genau zu meinen passt.
final
gibt Ihnen eine vom Compiler erzwungene Garantie, dass das Programm den Wert nicht ändert. Darauf würde ich nicht verzichten, nur weil der Programmierer möglicherweise den im Quellcode zugewiesenen Wert ändern möchte.gravity
des Spiels. Sie bedeuten nicht notwendigerweise, dass siegravity
auf jedem Planeten gleich sind ... Die gesunde Lösung besteht darin,gravity
eine Konstante zuplanet
erstellen , diese jedoch zu Beginn des relevanten Bereichs aus einer Datei oder Datenbank zu ziehen .Antworten:
In Java können statische Endkonstanten vom Compiler als ihre Werte in den Code kopiert werden, der sie verwendet . Infolgedessen wird die Konstante in diesem Code nicht aktualisiert, wenn Sie eine neue Version Ihres Codes freigeben und es eine Downstream-Abhängigkeit gibt, die die Konstante verwendet, es sei denn, der Downstream-Code wird neu kompiliert. Dies kann ein Problem sein, wenn sie dann diese Konstante mit Code verwenden, der den neuen Wert erwartet, da der Binärcode zwar richtig ist, der Quellcode aber nicht.
Dies ist eine Warze im Design von Java, da dies einer der wenigen Fälle (möglicherweise der einzige Fall) ist, in denen Quellkompatibilität und Binärkompatibilität nicht identisch sind. Mit Ausnahme dieses Falles können Sie eine Abhängigkeit durch eine neue API-kompatible Version ersetzen, ohne dass Benutzer der Abhängigkeit neu kompilieren müssen. Offensichtlich ist dies angesichts der Art und Weise, in der Java-Abhängigkeiten im Allgemeinen verwaltet werden, äußerst wichtig.
Erschwerend kommt hinzu, dass der Code im Stillen das Falsche tut und keine nützlichen Fehler erzeugt. Wenn Sie eine Abhängigkeit durch eine Version mit inkompatiblen Klassen- oder Methodendefinitionen ersetzen, erhalten Sie Klassenlade- oder Aufruffehler, die zumindest gute Hinweise auf das Problem liefern. Sofern Sie den Typ des Werts nicht geändert haben, wird dieses Problem nur als mysteriöses Laufzeitfehlverhalten angezeigt.
Ärgerlicher ist, dass die heutigen JVMs zur Laufzeit problemlos alle Konstanten inline setzen können, ohne dass Leistungseinbußen entstehen (abgesehen von der Notwendigkeit, die Klasse zu laden, die die Konstante definiert, die wahrscheinlich ohnehin geladen wird), leider die Semantik des Sprachdatums aus den Tagen vor den JITs . Und sie können die Sprache nicht ändern, weil dann mit früheren Compilern kompilierter Code nicht korrekt ist. Die Abwärtskompatibilität schlägt wieder zu.
Aus diesem Grund raten einige Leute, niemals einen statischen Endwert zu ändern. Für Bibliotheken, die zu unbekannten Zeiten weit verbreitet und auf unbekannte Weise aktualisiert werden könnten, ist dies eine gute Praxis.
In Ihrem eigenen Code, insbesondere an der Spitze der Abhängigkeitshierarchie, werden Sie wahrscheinlich damit durchkommen. Überlegen Sie in diesen Fällen jedoch, ob die Konstante wirklich öffentlich (oder geschützt) sein muss. Wenn die Konstante nur Paketsichtbarkeit ist, ist es abhängig von Ihren Umständen und Codestandards angemessen, dass das gesamte Paket immer auf einmal neu kompiliert wird und das Problem dann verschwindet. Wenn die Konstante privat ist, haben Sie kein Problem und können sie jederzeit ändern.
quelle
Alles in Ihrem Quellcode, einschließlich
const
deklarierter globaler Konstanten, kann sich mit einer neuen Version Ihrer Software ändern.Die Schlüsselwörter
const
(oderfinal
in Java) sollen dem Compiler signalisieren, dass sich diese Variable nicht ändert, während diese Instanz des Programms ausgeführt wird . Nichts mehr. Wenn Sie Nachrichten an den nächsten Betreuer senden möchten, verwenden Sie einen Kommentar in der Quelle. Dafür sind sie da.Ist ein viel besserer Weg, um mit Ihrem zukünftigen Selbst zu kommunizieren.
quelle
TaxRate
Seinpublic
macht mich nervös. Ich möchte sicher wissen, dass nur der Vertrieb von dieser Änderung betroffen ist und nicht auch unsere Lieferanten, die uns eine Steuer berechnen. Wer weiß, was in der Codebasis passiert ist, seit dieser Kommentar geschrieben wurde.public const
sollten Felder nur für Dinge verwendet werden, die sich niemals ändern werden, wie z. B. Math.pi. Wenn Sie eine Bibliothek erstellen, sollten Dinge, die sich während der Entwicklung oder mit einer neuen Version ändern könnenpublic static readonly
, so sein , dass keine Probleme mit Benutzern Ihrer Bibliothek auftreten.Wir müssen zwei Aspekte von Konstanten unterscheiden:
Und dann gibt es eine verwandte dritte Art: Variablen, deren Wert sich nicht ändert, dh Namen für einen Wert. Der Unterschied zwischen diesen unveränderlichen Variablen und einer Konstanten besteht darin, dass der Wert bestimmt / zugewiesen / initialisiert wird: Eine Variable wird zur Laufzeit initialisiert, der Wert einer Konstanten ist jedoch während der Entwicklung bekannt. Diese Unterscheidung ist etwas trübe, da ein Wert möglicherweise während der Entwicklung bekannt ist, aber tatsächlich nur während der Initialisierung erstellt wird.
Wenn der Wert einer Konstante jedoch zur Kompilierungszeit bekannt ist, kann der Compiler Berechnungen mit diesem Wert durchführen. Zum Beispiel hat die Java-Sprache das Konzept der konstanten Ausdrücke . Ein konstanter Ausdruck ist ein Ausdruck, der nur aus Literalen von Primitiven oder Zeichenfolgen, Operationen für konstante Ausdrücke (z. B. Casting, Addition, Zeichenfolgenverkettung) und konstanten Variablen besteht. [ JLS §15.28 ] Eine konstante Variable ist eine
final
Variable, die mit einem konstanten Ausdruck initialisiert wird. [JLS §4.12.4] Für Java ist dies eine Konstante zur Kompilierungszeit:Dies wird interessant, wenn eine konstante Variable in mehreren Kompilierungseinheiten verwendet wird und dann die Deklaration geändert wird. Erwägen:
A.java
:B.java
:Wenn wir diese Dateien kompilieren
B.class
, deklariert der Bytecode ein Feld,Y = 9
daB.Y
es sich um eine konstante Variable handelt.Wenn wir jedoch die
A.X
Variable auf einen anderen Wert ändern (z. B.X = 0
) und nur dieA.java
Datei neu kompilieren , wirdB.Y
weiterhin auf den alten Wert verwiesen. Dieser StatusA.X = 0, B.Y = 9
stimmt nicht mit den Deklarationen im Quellcode überein. Viel Spaß beim Debuggen!Dies bedeutet nicht, dass Konstanten niemals geändert werden sollten. Konstanten sind definitiv besser als magische Zahlen, die ohne Erklärung im Quellcode vorkommen. Doch der Wert ist der öffentlichen Konstanten Teil Ihrer öffentlichen API . Dies ist nicht spezifisch für Java, tritt jedoch auch in C ++ und anderen Sprachen mit separaten Kompilierungseinheiten auf. Wenn Sie diese Werte ändern, müssen Sie den gesamten abhängigen Code neu kompilieren, dh eine Neukompilierung durchführen.
Abhängig von der Art der Konstanten können sie zu falschen Annahmen der Entwickler geführt haben. Wenn diese Werte geändert werden, können sie einen Fehler auslösen. Beispielsweise könnte ein Satz von Konstanten so gewählt werden, dass sie bestimmte Bitmuster bilden, z
public static final int R = 4, W = 2, X = 1
. Wenn diese geändert werden, um eine andere Struktur wie zu bilden, wirdR = 0, W = 1, X = 2
vorhandener Code wieboolean canRead = perms & R
falsch. Und denken Sie nur an den Spaß, der sich daraus ergeben würde, wenn Sie sichInteger.MAX_VALUE
ändern würden! Hier gibt es keine Fehlerbehebung. Es ist nur wichtig zu wissen, dass der Wert einiger Konstanten wirklich wichtig ist und nicht einfach geändert werden kann.Aber für die Mehrheit der Konstanten ist das Ändern in Ordnung, solange die obigen Einschränkungen berücksichtigt werden. Eine Konstante kann sicher geändert werden, wenn die Bedeutung und nicht der spezifische Wert wichtig sind. Dies ist z. B. der Fall für Tunables wie
BORDER_WIDTH = 2
oderTIMEOUT = 60; // seconds
oder Vorlagen wieAPI_ENDPOINT = "https://api.example.com/v2/"
- obwohl einige oder alle davon möglicherweise in Konfigurationsdateien und nicht in Code angegeben werden sollten.quelle
Eine Konstante ist nur für die Laufzeit der Anwendung garantiert konstant . Solange dies zutrifft, gibt es keinen Grund, die Sprachfunktion nicht zu nutzen. Sie müssen nur wissen, welche Konsequenzen es hat, wenn Sie Konstanten- oder Compiler-Flags für denselben Zweck verwenden:
Nachdem wir eine Anwendung gepflegt hatten, die das Zeichnen von Begrenzungsrahmen für Formen einschaltete, damit wir debuggen konnten, wie sie gezeichnet wurden, stießen wir auf ein Problem. Nach dem Refactoring wurde der gesamte Code, der durch Compiler-Flags deaktiviert wurde, nicht kompiliert. Danach haben wir unsere Compiler-Flags absichtlich in Anwendungskonstanten geändert.
Ich sage das, um zu demonstrieren, dass es Kompromisse gibt. Das Gewicht einiger Boolescher Elemente würde nicht dazu führen, dass der Arbeitsspeicher der Anwendung erschöpft war oder zu viel Speicherplatz in Anspruch genommen wurde. Dies ist möglicherweise nicht der Fall, wenn Ihre Konstante wirklich ein großes Objekt ist, das im Wesentlichen alles in Ihrem Code verarbeitet. Wenn nicht alle Verweise auf ein Objekt entfernt werden müssen, nachdem es nicht mehr benötigt wird, ist das Objekt möglicherweise die Ursache für einen Speicherverlust.
Ich bin kein Fan von einfachen Pauschalaussagen, aber im Allgemeinen hat Ihr älterer Kollege Recht. Wenn sich etwas zwangsläufig häufig ändert, muss es möglicherweise ein konfigurierbares Element sein. Zum Beispiel könnten Sie Ihren Kollegen wahrscheinlich für eine Konstante überzeugen,
IsInDebugMode = true
wenn Sie einen Code vor Beschädigung schützen möchten. Einige Dinge müssen jedoch möglicherweise häufiger geändert werden, als Sie eine Anwendung freigeben. In diesem Fall müssen Sie diesen Wert zum richtigen Zeitpunkt ändern. Sie können das Beispiel einesTaxRate = .065
. Dies mag zum Zeitpunkt der Kompilierung Ihres Codes zutreffen, kann sich jedoch aufgrund neuer Gesetze ändern, bevor Sie die nächste Version Ihrer Anwendung veröffentlichen. Das ist etwas, das entweder von einem Speichermechanismus (wie einer Datei oder einer Datenbank) aktualisiert werden muss.quelle
#ifdef
s unterstützen? Da diese auf einer textuellen Ersetzung des Quellcodes basieren , sind sie nicht Teil der Programmiersprachensemantik. Beachten Sie, dass Java keinen Präprozessor hat.#ifdef
Flaggen. Während sie nicht Teil der C-Semantik sind, sind sie Teil von C #. Ich habe für den größeren Kontext des Sprachagnostizismus geschrieben.Das
const
,#define
oderfinal
ist ein Compiler-Hinweis (beachten Sie, dass das#define
nicht wirklich ein Hinweis ist, sondern ein Makro und bedeutend leistungsfähiger). Es zeigt an, dass sich der Wert während der Ausführung eines Programms nicht ändert und verschiedene Optimierungen vorgenommen werden können.Als Compiler-Hinweis führt der Compiler jedoch Dinge aus, die vom Programmierer nicht immer erwartet werden. Insbesondere wird javac a inline setzen,
static final int FOO = 42;
so dassFOO
der tatsächlich kompilierte Bytecode an jeder Stelle gelesen wird, an der er verwendet wird42
.Dies ist keine allzu große Überraschung, bis jemand den Wert ändert, ohne die andere Kompilierungseinheit (.java-Datei) neu zu kompilieren - und die
42
Reste im Bytecode (siehe, ist es möglich, das Inlining von statischen Endvariablen in javac zu deaktivieren? ).Etwas zu machen
static final
bedeutet, dass es das ist und für immer mehr, und es zu ändern, ist eine wirklich große Sache - besonders wenn es nicht alles istprivate
.Konstanten für Dinge wie
final static int ZERO = 0
sind kein Problem.final static double TAX_RATE = 0.55
(Abgesehen davon, dass es sich um Geld und Double handelt, ist es schlecht und sollte BigDecimal verwenden, aber dann ist es kein Primitiv und daher nicht inline), ist ein Problem und sollte mit großer Sorgfalt darauf untersucht werden, wo es verwendet wird.quelle
is a problem and should be examined with great care for where it is used.
Warum ist das ein Problem?Wie der Name schon sagt, sollten sich Konstanten während der Laufzeit nicht ändern, und meiner Meinung nach werden Konstanten so definiert, dass sie sich langfristig nicht ändern. ( Weitere Informationen finden Sie in dieser SO-Frage .)
Wenn Sie Flags benötigen (z. B. für den Entwicklungsmodus), sollten Sie stattdessen eine Konfigurationsdatei oder einen Startparameter verwenden (viele IDEs unterstützen die Konfiguration von Startparametern auf Projektbasis; siehe die entsprechende Dokumentation), um diesen Modus zu aktivieren. Auf diese Weise behalten Sie die Flexibilität, einen solchen Modus zu verwenden, und Sie können nicht vergessen, ihn jedes Mal zu ändern, wenn der Code produktiv wird.
quelle
Die Möglichkeit, zwischen den Läufen gewechselt zu werden, ist einer der wichtigsten Punkte, um eine Konstante in Ihrem Quellcode zu definieren!
Die Konstante gibt Ihnen einen genau definierten und dokumentierten Ort, an dem Sie den Wert jederzeit während der Lebensdauer Ihres Quellcodes ändern können. Es ist auch ein Versprechen, dass das Ändern der Konstante an diesem Ort tatsächlich alle Vorkommen dessen, wofür es steht, ändert.
Als negatives Beispiel: Es wäre nicht sinnvoll, eine Konstante zu haben,
TRUE
dietrue
in einer Sprache ausgewertet wird, die tatsächlich dastrue
Schlüsselwort enthält. Sie würden niemals, niemals, nicht einmal erklären,TRUE=false
außer als grausamer Witz.Natürlich gibt es auch andere Verwendungen von Konstanten, zum Beispiel das Kürzen von Code (
CO_NAME = 'My Great World Unique ACME Company'
), Vermeiden von Duplizierungen (PI=3.141
), Festlegen von Konventionen (TRUE=1
) oder was auch immer, aber eine definierte Position zum Ändern der Konstante ist mit Sicherheit eine der bekanntesten.quelle