Jak zdefiniować klasę stałych w Javie?

89

Załóżmy, że musisz zdefiniować klasę, w której wszystko co robi, to przechowywanie stałych.

public static final String SOME_CONST = "SOME_VALUE";

Jaki jest preferowany sposób zrobienia tego?

  1. Berło
  2. Klasa abstrakcyjna
  3. Klasa końcowa

Którego powinienem użyć i dlaczego?


Wyjaśnienia do niektórych odpowiedzi:

Wyliczenia - nie zamierzam używać wyliczeń, niczego nie wyliczam, zbieram tylko stałe, które nie są ze sobą w żaden sposób powiązane.

Interfejs - nie zamierzam ustawiać żadnej klasy jako implementującej interfejs. Po prostu chcesz użyć interfejsu zadzwonić stałe tak: ISomeInterface.SOME_CONST.

Yuval Adam
źródło
Jest tutaj podobna dyskusja: stackoverflow.com/questions/320588/… . Użyłbym klasy końcowej z prywatnym konstruktorem, aby nie można było jej utworzyć.
Dan Dyer
2
Przepraszam, ale „Nie zamierzam używać wyliczeń” zamienia to pytanie w „Jaki jest najlepszy sposób na zrobienie czegoś głupiego?”
cletus
Nie mówię, że zamierzasz zaimplementować interfejs. Ale nie ma sensu używać do tego interfejsu. Więc idź z ostatnią klasą
:)
Jaki jest problem z Enum? Możesz go zawsze używać do zbierania „pewnych stałych, które nie są ze sobą w żaden sposób powiązane”. Hmm?
gedevan
5
Koncepcyjnie wyliczenie jest złym wyborem, jeśli stałe nie są powiązane. Wyliczenie reprezentuje alternatywne wartości tego samego typu. Te stałe nie są alternatywami i mogą nawet nie być tego samego typu (niektóre mogą być ciągami znaków, niektórymi liczbami całkowitymi itp.)
Dan Dyer

Odpowiedzi:

92

Użyj ostatniej klasy. dla uproszczenia możesz następnie użyć importu statycznego, aby ponownie użyć wartości w innej klasie

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

w innej klasie:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}
user54579
źródło
4
Jakie są korzyści z utworzenia osobnej klasy w tym miejscu? Chociaż normalnie nie uważam interfejsu API Kalendarza za dobry przykład projektu, jest w porządku, jeśli chodzi o „stałe związane z kalendarzem znajdują się w klasie Calendar”.
Jon Skeet
7
korzyść polega na tym, że nie powielasz kodu, na wypadek, gdybyś musiał ponownie użyć stałych w więcej niż jednej klasie. Myślę, że łatwo można dostrzec zalety tego.
user54579
2
Dlaczego miałbyś zduplikować kod? Po prostu odnieś się do drugiej klasy. Uważam, że jest to stosunkowo rzadkie, że stała naprawdę występuje samodzielnie.
Jon Skeet
14
Dla lepszej czytelności miałbym również prywatnego konstruktora domyślnego bez treści (i pasującego komentarza).
Biegałem Bironem
5
Dość stara odpowiedź, a ten komentarz jest prawdopodobnie nie na miejscu, ale VALUE1.equals(variable)użyłbym go, ponieważ pozwala uniknąć NPE.
Aakash
38

Twoje wyjaśnienie brzmi: „Nie będę używał wyliczeń, niczego nie wyliczam, po prostu zbieram pewne stałe, które nie są ze sobą w żaden sposób powiązane”.

Jeśli stałe w ogóle nie są ze sobą powiązane, dlaczego chcesz je zebrać razem? Umieść każdą stałą w klasie, z którą jest najbliżej spokrewniona.

Jon Skeet
źródło
2
Jon - są powiązane w tym sensie, że wszystkie należą do tej samej funkcjonalności. Jednak nie są one wyliczeniem czegokolwiek… Nie posiadają własności, że przedmiot jest „jedną z tych rzeczy”.
Yuval Adam
13
W porządku. W takim przypadku po prostu umieść je w klasie najbliższej funkcji, z którą są związane.
Jon Skeet
26

Moje sugestie (malejąco według preferencji):

1) Nie rób tego . Utwórz stałe w aktualnej klasie, w której są najbardziej odpowiednie. Posiadanie klasy / interfejsu typu „worek stałych” nie jest zgodne z najlepszymi praktykami obiektowymi.

Ja i wszyscy inni od czasu do czasu ignorujemy # 1. Jeśli masz zamiar to zrobić, to:

2) Klasa końcowa z prywatnym konstruktorem Zapobiegnie to przynajmniej nadużywaniu twojego „worka stałych” poprzez rozszerzenie / zaimplementowanie go w celu uzyskania łatwego dostępu do stałych. (Wiem, że powiedziałeś, że tego nie zrobisz - ale to nie znaczy, że ktoś przyjdzie po Tobie)

3) Interfejs To zadziała, ale nie moje preferencje, podając ewentualne nadużycie w punkcie 2.

Ogólnie rzecz biorąc, to, że są to stałe, nie oznacza, że ​​nie powinieneś nadal stosować do nich normalnych zasad oo. Jeśli nikomu poza jedną klasą zależy na stałej - powinna być prywatna i należąca do tej klasy. Jeśli tylko testy dotyczą stałej - powinna znajdować się w klasie testowej, a nie w kodzie produkcyjnym. Jeśli stała jest zdefiniowana w wielu miejscach (nie tylko przypadkowo ta sama) - refaktoryzuj, aby wyeliminować powielanie. I tak dalej - traktuj je jak metodę.

kenj0418
źródło
2
Problem z 1 polega na tym, że czasami wstrzykujesz w swoim kodzie pewne zależności do innej klasy warstwy, aby pobrać stałą.
amdev,
Wszystkie pola w interfejsie są domyślnie publiczne, statyczne i ostateczne
Kodebetter
@Kodebetter Ale interfejsy można rozszerzyć / zaimplementować, aby dodać więcej pól.
Wit
12

Jak zauważa Joshua Bloch w Effective Java:

  • Interfejsy powinny być używane tylko do definiowania typów,
  • klasy abstrakcyjne nie zapobiegają instancjalizacji (można je tworzyć podklasy, a nawet sugerować, że zostały zaprojektowane do podklasy).

Możesz użyć Enum, jeśli wszystkie twoje stałe są powiązane (jak nazwy planet), umieścić stałe wartości w klasach, z którymi są powiązane (jeśli masz do nich dostęp), lub użyć nieinstanowalnej klasy narzędziowej (zdefiniuj prywatny domyślny konstruktor) .

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

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

Następnie, jak już wspomniano, możesz użyć importu statycznego, aby użyć stałych.

Sébastien RoccaSerra
źródło
7

Moją preferowaną metodą jest w ogóle tego nie robić. Wiek stałych prawie umarł, gdy Java 5 wprowadziła wyliczenia bezpieczne dla typów. Jeszcze wcześniej Josh Bloch opublikował (nieco bardziej rozwlekłą) wersję tego, która działała na Javie 1.4 (i wcześniejszych).

O ile nie potrzebujesz współdziałania z jakimś starszym kodem, naprawdę nie ma już powodu, aby używać nazwanych stałych typu String / integer.

cletus
źródło
2
Dobra odpowiedź, przykład byłby lepszy.
amdev,
3

Po prostu użyj ostatniej klasy.

Jeśli chcesz mieć możliwość dodawania innych wartości, użyj klasy abstrakcyjnej.

Używanie interfejsu nie ma większego sensu, interfejs ma określać kontrakt. Chcesz tylko zadeklarować pewne wartości stałe.

Megacan
źródło
3

Czy wyliczenia nie są najlepszym wyborem do tego typu rzeczy?

Boris Pavlović
źródło
2
99% czasu. Ale nie zawsze.
L. Holanda
3

enumsą w porządku. IIRC, jedna pozycja w efektywnej Javie (wyd. 2), ma enumstałe wyliczające standardowe opcje implementujące [słowo kluczowe Java] interfacedla dowolnej wartości.

Preferuję użycie [słowa kluczowego Java] zamiast interfacea final classdla stałych. Niejawnie otrzymujesz public static final. Niektórzy ludzie będą argumentować, że an interfacepozwala złym programistom na zaimplementowanie go, ale źli programiści będą pisać kod, który jest do niczego, bez względu na to, co robisz.

Który wygląda lepiej?

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

Lub:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}
Tom Hawtin - haczyk
źródło
ale źli programiści będą pisać kod, który jest do niczego, niezależnie od tego, co robisz. To tylko takie prawdziwe. Jeśli możesz podjąć kroki w celu złagodzenia złego programowania lub całkowitego wyeliminowania go, to masz za to pewną odpowiedzialność; być tak wyraźne, jak to tylko możliwe. Zły programista nie może przedłużyć końcowej klasy.
liltitus 27
1
@ liltitus27 W pewnym stopniu zgadzam się. Ale czy naprawdę chcesz, aby zniekształcony kod tylko powstrzymał jednego ze sposobów pisania złego kodu przez biednych programistów? Kod, który wyprodukują, nadal będzie beznadziejny, ale teraz twój kod jest mniej czytelny.
Tom Hawtin - tackline
1

Lub 4. Umieść je w klasie, która zawiera logikę używającą najczęściej stałych

... przepraszam, nie mogłem się oprzeć ;-)

Simon Groenewolt
źródło
3
To nie jest dobry pomysł. Tworzy to zależności między klasami, które nie powinny mieć żadnych.
Yuval Adam
Dlaczego nie? Powiedziałbym, że klasy, które używają tych samych stałych, i tak mają zależność ... I w jakiś sposób musi istnieć jedna klasa, która jest najbardziej zależna od tych stałych.
Simon Groenewolt
Gdyby istniała tylko jedna klasa korzystająca ze stałych, mogłoby to mieć sens.
Tom Hawtin - tackline
-1
  1. Jedną z wad prywatnego konstruktora jest to, że metoda nigdy nie mogła zostać przetestowana.

  2. Wyliczenie według koncepcji natury, które jest dobre do zastosowania w określonym typie domeny, zastosowanie go do zdecentralizowanych stałych nie wygląda wystarczająco dobrze

Pojęcie wyliczenia to „Wyliczenia to zbiory ściśle powiązanych elementów”.

  1. Rozszerzanie / implementowanie stałego interfejsu jest złą praktyką, trudno jest pomyśleć o wymaganiu rozszerzenia niezmiennej stałej zamiast odwoływania się do niej bezpośrednio.

  2. Jeśli zastosujesz narzędzie wysokiej jakości, takie jak SonarSource, istnieją zasady, które zmuszają programistę do porzucenia stałego interfejsu, jest to niezręczna rzecz, ponieważ wiele projektów korzysta ze stałego interfejsu i rzadko zdarza się, aby rzeczy "rozszerzające" miały miejsce na stałych interfejsach

Guizhou Feng
źródło
1
„prywatny konstruktor to istnienie metody, której nigdy nie można było sprawdzić”: nieprawda. Jeśli Ty (lub Twój szef) jesteście naprawdę paranoiczni, jeśli chodzi o raporty pokrycia kodu, które mają wynosić 100%, nadal można to przetestować, używając refleksji i upewniając się, że konstruktor zgłasza wyjątek. Ale IMHO, to tylko formalność i tak naprawdę nie musi być testowane. Jeśli Tobie (lub Twojemu zespołowi) zależy tylko na tym, co jest naprawdę ważne, 100% pokrycie linii nie jest konieczne.
L. Holanda