Wie lassen sich Konstanten am besten in Java implementieren?
Ein Ansatz, den wir wirklich vermeiden sollten : Verwendung von Schnittstellen zur Definition von Konstanten.
Eine Schnittstelle speziell für die Deklaration von Konstanten zu schaffen, ist wirklich das Schlimmste: Es macht den Grund zunichte, warum Schnittstellen entwickelt wurden: die Definition von Methodenkontrakten.
Selbst wenn eine Schnittstelle bereits existiert, um einen bestimmten Bedarf zu decken, macht es keinen Sinn, die Konstanten darin zu deklarieren, da Konstanten nicht Teil der API und des Vertrags sein sollten, der den Client-Klassen zur Verfügung gestellt wird.
Zur Vereinfachung haben wir im Großen und Ganzen 4 gültige Ansätze .
Mit static final String/Integer
Feld :
- 1) Verwendung einer Klasse, in der Konstanten deklariert sind, aber nicht nur.
- 1 Variante) Erstellung einer Klasse, die nur Konstanten deklariert.
Mit Java 5 enum
:
- 2) Deklaration des Enums in einer verwandten Zweckklasse (also als verschachtelte Klasse).
- 2 Variante) die Erstellung der Enum als eigenständige Klasse (also in einer eigenen Klassendatei definiert).
TLDR: Welches ist der beste Weg und wo befinden sich die Konstanten?
In den meisten Fällen ist die enum-Methode wahrscheinlich besser geeignet als die static final String/Integer
Weg und ich persönlich denke, dass die static final String/Integer
sollte nur verwendet werden, wenn es gute Gründe gibt, keine Enums zu verwenden.
Und darüber, wo wir die konstanten Werte deklarieren sollten, Die Idee besteht darin, zu suchen, ob es eine einzige existierende Klasse gibt, die eine spezifische und starke funktionale Kohäsion mit konstanten Werten besitzt. Wenn wir eine solche Klasse finden, sollten wir sie als Konstantenhalter verwenden . Andernfalls sollte die Konstante keiner bestimmten Klasse zugeordnet werden.
static final String
/ static final Integer
gegen enum
Enums Verwendung ist wirklich ein Weg, um stark berücksichtigt.
Enums haben einen großen Vorteil gegenüber String
o Integer
konstantes Feld.
Sie legen einen stärkeren Kompilierungszwang fest. Wenn Sie eine Methode definieren, die die Aufzählung als Parameter nimmt, können Sie nur einen in der Aufzählungsklasse definierten Aufzählungswert (oder null) übergeben.
Bei String und Integer können Sie sie durch beliebige Werte eines kompatiblen Typs ersetzen, und die Kompilierung ist in Ordnung, auch wenn der Wert keine definierte Konstante in der static final String
/ static final Integer
Felder.
Im Folgenden sind zum Beispiel zwei Konstanten in einer Klasse definiert als static final String
Felder:
public class MyClass{
public static final String ONE_CONSTANT = "value";
public static final String ANOTHER_CONSTANT = "other value";
. . .
}
Hier eine Methode, die eine dieser Konstanten als Parameter erwartet:
public void process(String constantExpected){
...
}
Sie können es auf diese Weise aufrufen:
process(MyClass.ONE_CONSTANT);
ou
process(MyClass.ANOTHER_CONSTANT);
Aber keine Kompilierungsbeschränkung hindert Sie daran, sie auf diese Weise aufzurufen:
process("a not defined constant value");
Der Fehler tritt nur zur Laufzeit auf, und auch nur dann, wenn Sie zu einem bestimmten Zeitpunkt den übertragenen Wert überprüfen.
Bei enum sind keine Prüfungen erforderlich, da der Kunde nur einen enum-Wert in einem enum-Parameter übergeben kann.
Hier zum Beispiel zwei Werte, die in einer Enum-Klasse definiert sind (also von vornherein konstant sind):
public enum MyEnum {
ONE_CONSTANT("value"), ANOTHER_CONSTANT(" another value");
private String value;
MyEnum(String value) {
this.value = value;
}
...
}
Hier eine Methode, die einen dieser Enum-Werte als Parameter erwartet:
public void process(MyEnum myEnum){
...
}
Sie können es auf diese Weise aufrufen:
process(MyEnum.ONE_CONSTANT);
ou
process(MyEnum.ANOTHER_CONSTANT);
Aber die Zusammenstellung wird Ihnen niemals erlauben, sie auf diese Weise aufzurufen:
process("a not defined constant value");
Wo sollten wir die Konstanten deklarieren?
Wenn Ihre Anwendung eine einzige bestehende Klasse enthält, die einen spezifischen und starken funktionalen Zusammenhang mit den konstanten Werten besitzt, erscheinen 1) und 2) intuitiver.
Im Allgemeinen erleichtert es die Verwendung der Konstanten, wenn diese in der Hauptklasse deklariert werden, die sie bearbeitet oder die einen Namen hat, der sehr naheliegend ist, um zu erraten, dass wir sie darin finden werden.
In der JDK-Bibliothek werden beispielsweise die Exponential- und Pi-Konstantenwerte in einer Klasse deklariert, die nicht nur Konstantendeklarationen ( java.lang.Math
).
public final class Math {
...
public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;
...
}
Die Kunden, die mathematische Funktionen verwenden, verlassen sich häufig auf die Math
Klasse. So können sie Konstanten leicht finden und sich auch merken, wo E
et PI
sind auf sehr natürliche Weise definiert.
Wenn Ihre Anwendung keine bestehende Klasse enthält, die einen sehr spezifischen und starken funktionalen Zusammenhang mit den konstanten Werten hat, erscheinen die Varianten 1) und 2) intuitiver.
Im Allgemeinen erleichtert es die Verwendung der Konstanten nicht, wenn diese in einer Klasse deklariert werden, die sie manipuliert, während wir auch 3 oder 4 andere Klassen haben, die sie ebenso manipulieren, und keine dieser Klassen scheint natürlicher zu sein als andere, um konstante Werte zu hosten.
Hier ist es sinnvoll, eine eigene Klasse zu definieren, die nur konstante Werte enthält.
Zum Beispiel in der JDK-Bibliothek, die java.util.concurrent.TimeUnit
enum wird nicht in einer bestimmten Klasse deklariert, da es nicht wirklich eine und nur eine JDK-spezifische Klasse gibt, die als die intuitivste erscheint, um sie zu enthalten:
public enum TimeUnit {
NANOSECONDS {
.....
},
MICROSECONDS {
.....
},
MILLISECONDS {
.....
},
SECONDS {
.....
},
.....
}
Viele Klassen, die in java.util.concurrent
sie verwenden: BlockingQueue
, ArrayBlockingQueue<E>
, CompletableFuture
, ExecutorService
... und eigentlich scheint keiner von ihnen besser geeignet zu sein, die Aufzählung zu enthalten.