Wie definieren Sie eine Klasse von Konstanten in Java?

89

Angenommen, Sie müssen eine Klasse definieren, die nur Konstanten enthält.

public static final String SOME_CONST = "SOME_VALUE";

Was ist der bevorzugte Weg, dies zu tun?

  1. Schnittstelle
  2. Abstrakte Klasse
  3. Abschlussklasse

Welches soll ich verwenden und warum?


Erläuterungen zu einigen Antworten:

Aufzählungen - Ich werde keine Aufzählungen verwenden, ich zähle nichts auf, sondern sammle nur einige Konstanten, die in keiner Weise miteinander verwandt sind.

Schnittstelle - Ich werde keine Klasse als eine festlegen, die die Schnittstelle implementiert. Ich möchte nur die Schnittstelle verwenden, um Konstanten wie folgt aufzurufen : ISomeInterface.SOME_CONST.

Yuval Adam
quelle
Es gibt hier eine ähnliche Diskussion: stackoverflow.com/questions/320588/… . Ich würde eine letzte Klasse mit einem privaten Konstruktor verwenden, damit sie nicht instanziiert werden kann.
Dan Dyer
2
Entschuldigung, aber "Ich werde keine Aufzählungen verwenden" verwandelt diese Frage in "Was ist der beste Weg, um etwas Dummes zu tun?"
Cletus
Ich sage nicht, dass Sie die Schnittstelle implementieren werden. Es macht jedoch keinen Sinn, dafür eine Schnittstelle zu verwenden. Also, geh mit der letzten Klasse
:)
Was ist das Problem mit Enum? Sie können es immer verwenden, um "einige Konstanten zu sammeln, die in keiner Weise miteinander verwandt sind". Hmm?
gedevan
5
Konzeptionell ist eine Aufzählung eine schlechte Wahl, wenn die Konstanten nicht verwandt sind. Eine Aufzählung steht für alternative Werte desselben Typs. Diese Konstanten sind keine Alternativen und sie können nicht einmal vom gleichen Typ sein (einige können Zeichenfolgen, einige Ganzzahlen usw. sein)
Dan Dyer

Antworten:

91

Verwenden Sie eine letzte Klasse. Der Einfachheit halber können Sie dann einen statischen Import verwenden, um Ihre Werte in einer anderen Klasse wiederzuverwenden

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

in einer anderen Klasse:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}
user54579
quelle
4
Wo ist der Vorteil, hier eine separate Klasse zu erstellen? Während ich die Kalender-API normalerweise nicht als gutes Beispiel für Design halte, ist sie in Bezug auf "kalenderbezogene Konstanten in der Kalenderklasse" in Ordnung.
Jon Skeet
7
Der Vorteil besteht darin, dass Code nicht dupliziert wird, falls Sie Konstanten in mehr als einer Klasse wiederverwenden müssen. Ich denke, Sie können den Vorteil davon leicht erkennen.
user54579
2
Warum sollten Sie Code duplizieren? Beziehen Sie sich einfach auf die andere Klasse. Ich finde es relativ selten, dass eine Konstante wirklich alleine steht.
Jon Skeet
14
Zur besseren Lesbarkeit hätte ich auch einen privaten Standardkonstruktor ohne Text (und einen passenden Kommentar).
Ran Biron
4
Ziemlich alte Antwort, und dieser Kommentar ist wahrscheinlich fehl am Platz, aber ich würde ihn verwenden, VALUE1.equals(variable)da er NPE vermeidet.
Aakash
38

In Ihrer Klarstellung heißt es: "Ich werde keine Aufzählungen verwenden, ich zähle nichts auf, sondern sammle nur einige Konstanten, die in keiner Weise miteinander verwandt sind."

Wenn die Konstanten überhaupt nicht miteinander verwandt sind, warum möchten Sie sie dann zusammen sammeln? Setzen Sie jede Konstante in die Klasse ein, mit der sie am engsten verwandt ist.

Jon Skeet
quelle
2
Jon - sie sind in dem Sinne verwandt, dass sie alle zur gleichen Funktionalität gehören. Sie sind jedoch keine Aufzählung von irgendetwas ... Sie besitzen nicht die Eigenschaft, dass ein Objekt "eines dieser Dinge" ist.
Yuval Adam
12
In Ordnung. In diesem Fall fügen Sie sie einfach in die Klasse ein, die der Funktionalität, auf die sie sich beziehen, am nächsten kommt.
Jon Skeet
26

Meine Vorschläge (in absteigender Reihenfolge der Präferenzen):

1) Tu es nicht . Erstellen Sie die Konstanten in der tatsächlichen Klasse dort, wo sie am relevantesten sind. Eine Klasse / Schnittstelle mit einer Menge Konstanten zu haben, folgt nicht wirklich den Best Practices von OO.

Ich und alle anderen ignorieren von Zeit zu Zeit die Nummer 1. Wenn du das machst, dann:

2) Abschlussklasse mit privatem Konstruktor Dies verhindert zumindest, dass jemand Ihren 'Beutel mit Konstanten' missbraucht, indem er ihn erweitert / implementiert, um einen einfachen Zugriff auf die Konstanten zu erhalten. (Ich weiß, dass du gesagt hast, du würdest das nicht tun - aber das bedeutet nicht, dass jemand mitkommt, nachdem du es nicht getan hast.)

3) Schnittstelle Dies wird funktionieren, aber nicht meine Präferenz, wenn ich den möglichen Missbrauch in # 2 erwähne.

Nur weil dies Konstanten sind, heißt das im Allgemeinen nicht, dass Sie keine normalen oo-Prinzipien auf sie anwenden sollten. Wenn sich nur eine Klasse um eine Konstante kümmert, sollte sie privat und in dieser Klasse sein. Wenn sich nur Tests um eine Konstante kümmern, sollte sie sich in einer Testklasse befinden, nicht im Produktionscode. Wenn eine Konstante an mehreren Stellen definiert ist (nicht nur versehentlich dieselbe) - Refactor, um Doppelarbeit zu vermeiden. Und so weiter - behandeln Sie sie wie eine Methode.

kenj0418
quelle
2
Das Problem der 1 ist, dass Sie manchmal einige Abhängigkeiten in eine andere Tier-Klasse in Ihren Code einfügen, um die Konstante abzurufen.
Amdev
Alle Felder in einer Schnittstelle sind implizit öffentlich, statisch und endgültig
Kodebetter
@Kodebetter Schnittstellen können jedoch erweitert / implementiert werden, um weitere Felder hinzuzufügen.
Wit
12

Wie Joshua Bloch in Effective Java feststellt:

  • Schnittstellen sollten nur zum Definieren von Typen verwendet werden.
  • abstrakte Klassen verhindern nicht die Instanz (sie können in Unterklassen unterteilt werden und schlagen sogar vor, dass sie für Unterklassen ausgelegt sind).

Sie können eine Aufzählung verwenden, wenn alle Ihre Konstanten verwandt sind (wie Planetennamen), die Konstantenwerte in Klassen einfügen, auf die sie sich beziehen (wenn Sie Zugriff darauf haben), oder eine nicht instanziierbare Dienstprogrammklasse verwenden (einen privaten Standardkonstruktor definieren). .

class SomeConstants
{
    // Prevents instanciation of myself and my subclasses
    private SomeConstants() {}

    public final static String TOTO = "toto";
    public final static Integer TEN = 10;
    //...
}

Dann können Sie, wie bereits erwähnt, statische Importe verwenden, um Ihre Konstanten zu verwenden.

Sébastien RoccaSerra
quelle
7

Meine bevorzugte Methode ist es, das überhaupt nicht zu tun. Das Alter der Konstanten starb so ziemlich, als Java 5 typsichere Aufzählungen einführte. Und schon vorher veröffentlichte Josh Bloch eine (etwas wortreichere) Version davon, die auf Java 1.4 (und früher) funktionierte.

Sofern Sie keine Interoperabilität mit einem älteren Code benötigen, gibt es keinen Grund mehr, benannte String / Integer-Konstanten zu verwenden.

Cletus
quelle
1
Gute Antwort, ein Beispiel wäre besser.
Amdev
3

Verwenden Sie einfach die letzte Klasse.

Wenn Sie andere Werte hinzufügen möchten, verwenden Sie eine abstrakte Klasse.

Es macht nicht viel Sinn, eine Schnittstelle zu verwenden, eine Schnittstelle soll einen Vertrag spezifizieren. Sie möchten nur einige konstante Werte deklarieren.

Megacan
quelle
3

Sind Enums nicht die beste Wahl für solche Sachen?

Boris Pavlović
quelle
2
99% der Zeit. Aber nicht immer.
L. Holanda
3

enums sind in Ordnung. IIRC, ein Element in effektivem Java (2. Ausgabe), enthält enumKonstanten, die Standardoptionen auflisten, die ein [Java-Schlüsselwort] interfacefür einen beliebigen Wert implementieren .

Ich bevorzuge die Verwendung eines [Java-Schlüsselworts] interfacegegenüber einem final classfor-Konstanten. Sie erhalten implizit die public static final. Einige Leute werden argumentieren, dass interfacees schlechten Programmierern erlaubt, es zu implementieren, aber schlechte Programmierer werden Code schreiben, der nervt, egal was Sie tun.

Welches sieht besser aus?

public final class SomeStuff {
     private SomeStuff() {
         throw new Error();
     }
     public static final String SOME_CONST = "Some value or another, I don't know.";
}

Oder:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}
Tom Hawtin - Tackline
quelle
Aber schlechte Programmierer werden Code schreiben, der nervt, egal was Sie tun. Das ist nur so wahr. Wenn Sie Maßnahmen ergreifen können, um schlechte Programmierung zu mildern oder ganz zu beseitigen, haben Sie eine gewisse Verantwortung dafür. sei so explizit wie möglich. Ein schlechter Programmierer kann eine Abschlussklasse nicht verlängern.
Liltitus27
1
@ liltitus27 Bis zu einem gewissen Grad stimme ich zu. Aber wollen Sie wirklich hässlichen Code, um zu verhindern, dass arme Programmierer schlechten Code schreiben? Der Code, den sie produzieren, ist immer noch hoffnungslos, aber jetzt ist Ihr Code weniger lesbar.
Tom Hawtin - Tackline
1

Oder 4. Fügen Sie sie in die Klasse ein, die die Logik enthält, die die Konstanten am häufigsten verwendet

... sorry, konnte nicht widerstehen ;-)

Simon Groenewolt
quelle
3
Das ist keine gute Idee. Dies schafft Abhängigkeiten zwischen Klassen, die keine haben sollten.
Yuval Adam
Warum nicht? Ich würde sagen, dass Klassen, die dieselben Konstanten verwenden, sowieso eine Abhängigkeit haben ... Und irgendwie muss es eine Klasse geben, die am stärksten von diesen Konstanten abhängig ist.
Simon Groenewolt
Wenn nur eine Klasse die Konstanten verwenden würde, könnte dies sinnvoll sein.
Tom Hawtin - Tackline
-1
  1. Einer der Nachteile des privaten Konstruktors ist, dass die existierende Methode niemals getestet werden konnte.

  2. Aufzählung nach dem Naturkonzept gut, um in einem bestimmten Domänentyp anzuwenden, wendet es auf dezentrale Konstanten an, sieht nicht gut genug aus

Das Konzept von Enum lautet "Aufzählungen sind Sätze eng verwandter Elemente".

  1. Das Erweitern / Implementieren einer konstanten Schnittstelle ist eine schlechte Praxis. Es ist schwierig, über die Anforderung nachzudenken, eine unveränderliche Konstante zu erweitern, anstatt direkt darauf zu verweisen.

  2. Wenn Sie ein Qualitätswerkzeug wie SonarSource anwenden, gibt es Regeln, die Entwickler dazu zwingen, eine konstante Schnittstelle zu löschen. Dies ist eine unangenehme Sache, da viele Projekte die konstante Schnittstelle genießen und selten "erweiterte" Dinge auf konstanten Schnittstellen passieren

Guizhou Feng
quelle
1
"Privater Konstruktor ist die Existenz einer Methode, die niemals getestet werden könnte": nicht wahr. Wenn Sie (oder Ihr Chef) wirklich paranoid sind, wenn Code-Coverage-Berichte zu 100% vorliegen, können Sie dies dennoch testen, indem Sie Reflection verwenden und sicherstellen, dass der Konstruktor eine Ausnahme auslöst. Aber meiner Meinung nach ist dies nur Formalität und muss nicht wirklich getestet werden. Wenn Sie (oder Ihr Team) sich nur wirklich darum kümmern, was wirklich wichtig ist, ist eine 100% ige Leitungsabdeckung nicht erforderlich.
L. Holanda